All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support
       [not found] <20190122121413.31437-1-ysato@users.sourceforge.jp>
@ 2019-03-02  6:21 ` Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 01/11] target/rx: TCG Translation Yoshinori Sato
                     ` (13 more replies)
  0 siblings, 14 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

Hello.
This patch series is added Renesas RX target emulation.

My git repository is bellow.
git://git.pf.osdn.net/gitroot/y/ys/ysato/qemu.git

Since my understanding is not enough,
I want many comments to make this a good one.

Thanks.

Changes v2
Rewrite translate. using decodetree.py

Yoshinori Sato (11):
  target/rx: TCG Translation
  target/rx: TCG helper
  target/rx: CPU definition
  target/rx: RX disassembler
  target/rx: miscellaneous functions
  RX62N interrupt contorol uint
  RX62N internal timer modules
  RX62N internal serial communication interface
  RX Target hardware definition
  Add rx-softmmu
  MAINTAINERS: Add RX entry.

 MAINTAINERS                    |   20 +
 arch_init.c                    |    2 +
 configure                      |    8 +
 default-configs/rx-softmmu.mak |    7 +
 hw/char/Makefile.objs          |    2 +-
 hw/char/renesas_sci.c          |  288 ++++++
 hw/intc/Makefile.objs          |    1 +
 hw/intc/rx_icu.c               |  323 ++++++
 hw/rx/Makefile.objs            |    1 +
 hw/rx/rx62n.c                  |  227 ++++
 hw/rx/rxqemu.c                 |  100 ++
 hw/timer/Makefile.objs         |    2 +
 hw/timer/renesas_cmt.c         |  235 +++++
 hw/timer/renesas_tmr.c         |  412 ++++++++
 include/disas/bfd.h            |    5 +
 include/hw/char/renesas_sci.h  |   42 +
 include/hw/intc/rx_icu.h       |   49 +
 include/hw/rx/rx.h             |    7 +
 include/hw/rx/rx62n.h          |   54 +
 include/hw/timer/renesas_cmt.h |   33 +
 include/hw/timer/renesas_tmr.h |   42 +
 include/sysemu/arch_init.h     |    1 +
 target/rx/Makefile.objs        |   11 +
 target/rx/cpu-qom.h            |   52 +
 target/rx/cpu.c                |  224 ++++
 target/rx/cpu.h                |  214 ++++
 target/rx/disas.c              | 1570 ++++++++++++++++++++++++++++
 target/rx/gdbstub.c            |  113 ++
 target/rx/helper.c             |  252 +++++
 target/rx/helper.h             |   39 +
 target/rx/insns.decode         |  336 ++++++
 target/rx/monitor.c            |   38 +
 target/rx/op_helper.c          |  602 +++++++++++
 target/rx/translate.c          | 2220 ++++++++++++++++++++++++++++++++++++++++
 34 files changed, 7531 insertions(+), 1 deletion(-)
 create mode 100644 default-configs/rx-softmmu.mak
 create mode 100644 hw/char/renesas_sci.c
 create mode 100644 hw/intc/rx_icu.c
 create mode 100644 hw/rx/Makefile.objs
 create mode 100644 hw/rx/rx62n.c
 create mode 100644 hw/rx/rxqemu.c
 create mode 100644 hw/timer/renesas_cmt.c
 create mode 100644 hw/timer/renesas_tmr.c
 create mode 100644 include/hw/char/renesas_sci.h
 create mode 100644 include/hw/intc/rx_icu.h
 create mode 100644 include/hw/rx/rx.h
 create mode 100644 include/hw/rx/rx62n.h
 create mode 100644 include/hw/timer/renesas_cmt.h
 create mode 100644 include/hw/timer/renesas_tmr.h
 create mode 100644 target/rx/Makefile.objs
 create mode 100644 target/rx/cpu-qom.h
 create mode 100644 target/rx/cpu.c
 create mode 100644 target/rx/cpu.h
 create mode 100644 target/rx/disas.c
 create mode 100644 target/rx/gdbstub.c
 create mode 100644 target/rx/helper.c
 create mode 100644 target/rx/helper.h
 create mode 100644 target/rx/insns.decode
 create mode 100644 target/rx/monitor.c
 create mode 100644 target/rx/op_helper.c
 create mode 100644 target/rx/translate.c

-- 
2.11.0

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 01/11] target/rx: TCG Translation
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 02/11] target/rx: TCG helper Yoshinori Sato
                     ` (12 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

This part only supported RXv1 instructions.
Instruction manual.
https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01us0032ej0120_rxsm.pdf

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 target/rx/insns.decode |  336 ++++++++
 target/rx/translate.c  | 2220 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2556 insertions(+)
 create mode 100644 target/rx/insns.decode
 create mode 100644 target/rx/translate.c

diff --git a/target/rx/insns.decode b/target/rx/insns.decode
new file mode 100644
index 0000000000..59de4566e0
--- /dev/null
+++ b/target/rx/insns.decode
@@ -0,0 +1,336 @@
+&bcnd		cd dsp
+&jdsp		dsp
+&jreg		rs
+&rr		rd rs
+&ri		rd imm
+&rrr		rd rs rs2
+&rri		rd imm rs2 len
+&rrli		rd rs2 imm
+&rm		rd rs ld mi
+&mi		rs ld mi imm
+&mr		rs ld mi rs2
+
+########
+
+%b2_r_0		16:4
+%b2_li_2	18:2 !function=li
+%b2_li_8	24:2 !function=li
+%b2_dsp5_3	23:4 19:1
+
+@b2_rds		.... .... .... rd:4		&rr rs=255
+@b2_rds_li	.... .... .... rd:4		&rrli rs2=%b2_r_0 imm=%b2_li_8
+@b2_rds_uimm4	.... .... imm:4 rd:4		&rri rs2=255 len=2
+@b2_rds_imm5	.... ... imm:5 rd:4		&rri rs2=255 len=2
+@b2_rd_rs_li	.... .... rs2:4 rd:4		&rrli imm=%b2_li_8
+@b2_rd_ld_ub	.... .. ld:2 rs:4 rd:4		&rm mi=4
+@b2_ld_imm3	.... .. ld:2 rs:4 . imm:3	&mi mi=4
+
+########
+
+%b3_r_0		8:4
+%b3_li_10	18:2 !function=li
+%b3_dsp5_8	23:1 16:4
+
+@b3_rd_rs	.... .... .... .... rs:4 rd:4		&rr
+@b3_rd_li	.... .... .... .... .... rd:4 \
+		&rrli rs2=%b3_r_0 imm=%b3_li_10
+@b3_rd_ld	.... .... mi:2 .... ld:2 rs:4 rd:4	&rm
+@b3_rd_ld_ub	.... .... .... .. ld:2 rs:4 rd:4	&rm mi=4
+@b3_rd_ld_ul	.... .... .... .. ld:2 rs:4 rd:4	&rm mi=2
+@b3_rd_rs_rs2	.... .... .... rd:4 rs:4 rs2:4		&rrr
+@b3_ld_rs2	.... .... .... .. ld:2 rs:4 rs2:4	&mr
+@b3_rds_imm5	.... .... ....... imm:5 rd:4		&rri rs2=%b3_r_0 len=2
+@b3_rd_rs_imm5	.... .... ... imm:5 rs2:4 rd:4		&rri len=2
+
+########
+
+%b4_li_18	18:2 !function=li
+
+@b4_rd_ldmi	.... .... mi:2 .... ld:2 .... .... rs:4 rd:4	&rm
+
+########
+
+ABS_rr		0111 1110 0010 ....			@b2_rds
+ABS_rr		1111 1100 0000 1111 .... ....		@b3_rd_rs
+
+ADC_rli		1111 1101 0111 ..00 0010 ....		@b3_rd_li
+ADC_rr		1111 1100 0000 1011 .... ....		@b3_rd_rs
+# Note only mi==2 allowed.
+ADC_rl		0000 0110 ..10 00.. 0000 0010 .... ....	@b4_rd_ldmi
+
+ADD_rri		0110 0010 .... ....			@b2_rds_uimm4
+ADD_rrli	0111 00.. .... ....			@b2_rd_rs_li
+ADD_rl		0100 10.. .... ....			@b2_rd_ld_ub
+ADD_rl		0000 0110 ..00 10.. .... ....		@b3_rd_ld
+ADD_rrr		1111 1111 0010 .... .... ....		@b3_rd_rs_rs2
+
+AND_ri		0110 0100 .... ....			@b2_rds_uimm4
+AND_rli		0111 01.. 0010 ....			@b2_rds_li
+AND_rl		0101 00.. .... ....			@b2_rd_ld_ub
+AND_rl		0000 0110 ..01 00.. .... ....		@b3_rd_ld
+AND_rrr		1111 1111 0100 .... .... ....		@b3_rd_rs_rs2
+
+BCLR_li		1111 00.. .... 1...			@b2_ld_imm3
+BCLR_ri		0111 101. .... ....			@b2_rds_imm5
+BCLR_lr		1111 1100 0110 01.. .... ....		@b3_ld_rs2 mi=4
+
+BCnd_s		0001 cd:1 dsp:3				&bcnd
+BCnd_b		0010 cd:4 dsp:8				&bcnd
+BCnd_w		0011 101 cd:1 dsp:16			&bcnd
+
+# Note that BNOT has cd = 15
+BMCnd_BNOT_mi	1111 1100 111 imm:3 ld:2 rd:4 cd:4
+BMCnd_BNOT_ri	1111 1101 111 imm:5 cd:4 rd:4
+
+BNOT_lr		1111 1100 0110 11.. .... ....		@b3_ld_rs2 mi=2
+
+BRA_s		0000 1 dsp:3				&jdsp
+#BRA_b		0010 1110 dsp:8		# overlaps BCnd_b
+BRA_w		0011 1000 dsp:16			&jdsp
+BRA_a		0000 0100 dsp:24			&jdsp
+BRA_l		0111 1111 0100 rd:4
+
+BRK		0000 0000
+
+BSET_li		1111 00.. .... 0...			@b2_ld_imm3
+BSET_ri		0111 100. .... ....			@b2_rds_imm5
+BSET_lr		1111 1100 0110 00.. .... ....		@b3_ld_rs2 mi=4
+
+BSR_w		0011 1001 dsp:16			&jdsp
+BSR_a		0000 0101 dsp:24			&jdsp
+BSR_l		0111 1111 0101 rd:4
+
+BTST_li		1111 01.. .... 0...			@b2_ld_imm3
+BTST_ri		0111 110. .... ....			@b2_rds_imm5
+BTST_lr		1111 1100 0110 10.. .... ....		@b3_ld_rs2 mi=4
+
+CLRPSW		0111 1111 1011 cb:4
+
+CMP_ri		0110 0001 .... ....			@b2_rds_uimm4
+CMP_ri		0111 0101 0101 rs2:4 imm:8		&rri rd=0 len=3
+CMP_rli		0111 01.. 0000 rs2:4			&rrli imm=%b2_li_8 rd=0
+CMP_rl		0100 01.. .... ....			@b2_rd_ld_ub
+CMP_rl		0000 0110 ..00 01.. .... ....		@b3_rd_ld
+
+DIV_ri		1111 1101 0111 ..00 1000 ....		@b3_rd_li
+DIV_rl		1111 1100 0010 00.. .... ....		@b3_rd_ld_ub
+DIV_rl		0000 0110 ..10 00.. 0000 1000 .... ....	@b4_rd_ldmi
+
+DIVU_ri		1111 1101 0111 ..00 1001 ....		@b3_rd_li
+DIVU_rl		1111 1100 0010 01.. .... ....		@b3_rd_ld_ub
+DIVU_rl		0000 0110 ..10 00.. 0000 1001 .... ....	@b4_rd_ldmi
+
+EMUL_ri		1111 1101 0111 ..00 0110 ....		@b3_rd_li
+EMUL_rl		1111 1100 0001 10.. .... ....		@b3_rd_ld_ub
+EMUL_rl		0000 0110 ..10 00.. 0000 0110 .... ....	@b4_rd_ldmi
+
+EMULU_ri	1111 1101 0111 ..00 0111 ....		@b3_rd_li
+EMULU_rl	1111 1100 0001 11.. .... ....		@b3_rd_ld_ub
+EMULU_rl	0000 0110 ..10 00.. 0000 0111 .... ....	@b4_rd_ldmi
+
+FADD_ri		1111 1101 0111 0010 0010 rd:4
+FADD_rl		1111 1100 1000 10.. .... ....		@b3_rd_ld_ul
+
+FCMP_ri		1111 1101 0111 0010 0001 rd:4
+FCMP_rl		1111 1100 1000 01.. .... ....		@b3_rd_ld_ul
+
+FDIV_ri		1111 1101 0111 0010 0100 rd:4
+FDIV_rl		1111 1100 1001 00.. .... ....		@b3_rd_ld_ul
+
+FMUL_ri		1111 1101 0111 0010 0011 rd:4
+FMUL_rl		1111 1100 1000 11.. .... ....		@b3_rd_ld_ul
+
+FSUB_ri		1111 1101 0111 0010 0000 rd:4
+FSUB_rl		1111 1100 1000 00.. .... ....		@b3_rd_ld_ul
+
+FTOI		1111 1100 1001 01.. .... ....		@b3_rd_ld_ul
+
+INT		0111 0101 0110 0000 imm:8
+
+ITOF		1111 1100 0100 01.. .... ....		@b3_rd_ld_ub
+ITOF		0000 0110 ..10 00.. 0001 0001 .... ....	@b4_rd_ldmi
+
+JMP		0111 1111 0000 rs:4			&jreg
+JSR		0111 1111 0001 rs:4			&jreg
+
+MACHI		1111 1101 0000 0100 rs:4 rs2:4
+MACLO		1111 1101 0000 0101 rs:4 rs2:4
+
+MAX_ri		1111 1101 0111 ..00 0100 ....		@b3_rd_li
+MAX_rl		1111 1100 0001 00.. .... ....		@b3_rd_ld_ub
+MAX_rl		0000 0110 ..10 00.. 0000 0100 .... ....	@b4_rd_ldmi
+
+MIN_ri		1111 1101 0111 ..00 0101 ....		@b3_rd_li
+MIN_rl		1111 1100 0001 01.. .... ....		@b3_rd_ld_ub
+MIN_rl		0000 0110 ..10 00.. 0000 0101 .... ....	@b4_rd_ldmi
+
+MOV_mr		1000 0 .... rd:3 . rs:3			dsp=%b2_dsp5_3 sz=0
+MOV_mr		1001 0 .... rd:3 . rs:3			dsp=%b2_dsp5_3 sz=1
+MOV_mr		1010 0 .... rd:3 . rs:3			dsp=%b2_dsp5_3 sz=2
+MOV_rm		1000 1 .... rs:3 . rd:3			dsp=%b2_dsp5_3 sz=0
+MOV_rm		1001 1 .... rs:3 . rd:3			dsp=%b2_dsp5_3 sz=1
+MOV_rm		1010 1 .... rs:3 . rd:3			dsp=%b2_dsp5_3 sz=2
+MOV_ri		0110 0110 imm:4 rd:4 len=2
+MOV_mi		0011 1100 . rd:3 .... imm:8		sz=0 dsp=%b3_dsp5_8
+MOV_mi		0011 1101 . rd:3 .... imm:8		sz=1 dsp=%b3_dsp5_8
+MOV_mi		0011 1110 . rd:3 .... imm:8		sz=2 dsp=%b3_dsp5_8
+MOV_ri		0111 0101 0100 rd:4 imm:8 len=3
+MOV_rli		1111 1011 rd:4 .. 10			imm=%b2_li_2
+MOV_mli		1111 1000 rd:4 .. sz:2			dsp=0 imm=%b2_li_2 ld=0
+MOV_mli		1111 1001 rd:4 .. sz:2 dsp:8		imm=%b3_li_10 ld=1
+MOV_mli		1111 1010 rd:4 .. sz:2 dsp:16		imm=%b4_li_18 ld=2
+MOV_ra		1111 1110 01 sz:2 ri:4 rb:4 rd:4
+MOV_ar		1111 1110 00 sz:2 ri:4 rb:4 rs:4
+# Note ldd=3 and lds=3 indicate register src or dst
+MOV_ll		1100 ldd:2 lds:2 rs:4 rd:4		sz=0
+MOV_ll		1101 ldd:2 lds:2 rs:4 rd:4		sz=1
+MOV_ll		1110 ldd:2 lds:2 rs:4 rd:4		sz=2
+MOV_pr		1111 1101 0010 0 ad:1 sz:2 rd:4 rs:4
+MOV_rp		1111 1101 0010 1 ad:1 sz:2 rd:4 rs:4
+
+MOVU_rm		1011 sz:1 ... . rs:3 . rd:3		dsp=%b2_dsp5_3
+MOVU_rl		0101 1 sz:1 ld:2 rs:4 rd:4
+MOVU_ra		1111 1110 110 sz:1 ri:4 rb:4 rd:4
+MOVU_rp		1111 1101 0011 1 ad:1 0 sz:1 rs:4 rd:4
+
+MUL_ri		0110 0011 .... ....			@b2_rds_uimm4
+MUL_rli		0111 01.. 0001 ....			@b2_rds_li
+MUL_rl		0100 11.. .... ....			@b2_rd_ld_ub
+MUL_rl		0000 0110 ..00 11.. .... ....		@b3_rd_ld
+MUL_rrr		1111 1111 0011 .... .... ....		@b3_rd_rs_rs2
+
+MULHI		1111 1101 0000 0000 rs:4 rs2:4
+MULLO		1111 1101 0000 0001 rs:4 rs2:4
+
+MVFACHI		1111 1101 0001 1111 0000 rd:4
+MVFACMI		1111 1101 0001 1111 0010 rd:4
+
+MVFC		1111 1101 0110 1010 cr:4 rd:4
+
+MVTACHI		1111 1101 0001 0111 0000 rs:4
+MVTACLO		1111 1101 0001 0111 0001 rs:4
+
+MVTC_i		1111 1101 0111 ..11 0000 cr:4		imm=%b3_li_10
+MVTC_r		1111 1101 0110 1000 rs:4 cr:4
+
+MVTIPL		0111 0101 0111 0000 0000 imm:4
+
+NEG_rr		0111 1110 0001 ....			@b2_rds
+NEG_rr		1111 1100 0000 0111 .... ....		@b3_rd_rs
+
+NOP		0000 0011
+
+NOT_rr		0111 1110 0000 ....			@b2_rds
+NOT_rr		1111 1100 0011 1011 .... ....		@b3_rd_rs
+
+OR_ri		0110 0101 .... ....			@b2_rds_uimm4
+OR_rli		0111 01.. 0011 ....			@b2_rds_li
+OR_rl		0101 01.. .... ....			@b2_rd_ld_ub
+OR_rl		0000 0110 .. 0101 .. .... ....		@b3_rd_ld
+OR_rrr		1111 1111 0101 .... .... ....		@b3_rd_rs_rs2
+
+POP		0111 1110 1011 rd:4
+POPC		0111 1110 1110 cr:4
+POPM		0110 1111 rd:4 rd2:4
+
+PUSH_r		0111 1110 1000 rs:4			sz=0
+PUSH_r		0111 1110 1001 rs:4			sz=1
+PUSH_r		0111 1110 1010 rs:4			sz=2
+PUSH_m		1111 01 ld:2 rs:4 10 sz:2
+PUSHC		0111 1110 1100 cr:4
+PUSHM		0110 1110 rs:4 rs2:4
+
+RACW		1111 1101 0001 1000 000 imm:1 0000
+
+REVL		1111 1101 0110 0111 rs:4 rd:4
+REVW		1111 1101 0110 0101 rs:4 rd:4
+
+# Note that sz=3 overlaps SMOVF
+RMPA		0111 1111 1000 1100			sz=0
+RMPA		0111 1111 1000 1101			sz=1
+RMPA		0111 1111 1000 1110			sz=2
+
+ROLC		0111 1110 0101 ....			@b2_rds
+RORC		0111 1110 0100 ....			@b2_rds
+
+ROTL_ri		1111 1101 0110 111. .... ....		@b3_rds_imm5
+ROTL_rr		1111 1101 0110 0110 .... ....		@b3_rd_rs
+
+ROTR_ri		1111 1101 0110 110. .... ....		@b3_rds_imm5
+ROTR_rr		1111 1101 0110 0100 .... ....		@b3_rd_rs
+
+ROUND		1111 1100 1001 10 ld:2 rs:4 rd:4
+
+RTE		0111 1111 1001 0101
+
+RTFI		0111 1111 1001 0100
+
+RTS		0000 0010
+
+RTSD_i		0110 0111 imm:8
+RTSD_irr	0011 1111 rd:4 rd2:4 imm:8
+
+SAT		0111 1110 0011 ....			@b2_rds
+SATR		0111 1111 1001 0011
+
+SBB_rr		1111 1100 0000 0011 .... ....		@b3_rd_rs
+# Note only mi==2 allowed.
+SBB_rl		0000 0110 ..10 00.. 0000 0000 .... ....	@b4_rd_ldmi
+
+SCCnd		1111 1100 1101 sz:2 ld:2 rd:4 cd:4
+
+SCMPU		0111 1111 1000 0011
+
+SETPSW		0111 1111 1010 cb:4
+
+SHAR_rri	0110 101. .... ....			@b2_rds_imm5
+SHAR_rri	1111 1101 101. .... .... ....		@b3_rd_rs_imm5
+SHAR_rr		1111 1101 0110 0001 .... ....		@b3_rd_rs
+
+SHLL_rri	0110 110. .... ....			@b2_rds_imm5
+SHLL_rri	1111 1101 110. .... .... ....		@b3_rd_rs_imm5
+SHLL_rr		1111 1101 0110 0010 .... ....		@b3_rd_rs
+
+SHLR_rri	0110 100. .... ....			@b2_rds_imm5
+SHLR_rri	1111 1101 100. .... .... ....		@b3_rd_rs_imm5
+SHLR_rr		1111 1101 0110 0000 .... ....		@b3_rd_rs
+
+SMOVB		0111 1111 1000 1011
+SMOVF		0111 1111 1000 1111
+SMOVU		0111 1111 1000 0111
+
+# Note that sz=3 overlaps SMOVB
+SSTR		0111 1111 1000 1000			sz=0
+SSTR		0111 1111 1000 1001			sz=1
+SSTR		0111 1111 1000 1010			sz=2
+
+STNZ		1111 1101 0111 ..00 1111 ....		@b3_rd_li
+STZ		1111 1101 0111 ..00 1110 ....		@b3_rd_li
+
+SUB_ri		0110 0000 .... ....			@b2_rds_uimm4
+SUB_rl		0100 00.. .... ....			@b2_rd_ld_ub
+SUB_rl		0000 0110 ..00 00.. .... ....		@b3_rd_ld
+SUB_rrr		1111 1111 0000 .... .... ....		@b3_rd_rs_rs2
+
+# Note that sz=3 overlaps SCMPU
+SUNTIL		0111 1111 1000 0000			sz=0
+SUNTIL		0111 1111 1000 0001			sz=1
+SUNTIL		0111 1111 1000 0010			sz=2
+
+# Note that sz=3 overlaps SMOVU
+SWHILE		0111 1111 1000 0100			sz=0
+SWHILE		0111 1111 1000 0101			sz=1
+SWHILE		0111 1111 1000 0110			sz=2
+
+TST_rli		1111 1101 0111 ..00 1100 ....		@b3_rd_li
+TST_rl		1111 1100 0011 00.. .... ....		@b3_rd_ld_ub
+TST_rl		0000 0110 ..10 00.. 0000 1100 .... .... @b4_rd_ldmi
+
+WAIT		0111 1111 1001 0110
+
+XCHG_rl		1111 1100 0100 00.. .... ....		@b3_rd_ld_ub
+XCHG_rl		0000 0110 ..10 00.. 0001 0000 .... ....	@b4_rd_ldmi
+
+XOR_rli		1111 1101 0111 ..00 1101 ....		@b3_rd_li
+XOR_rl		1111 1100 0011 01.. .... ....		@b3_rd_ld_ub
+XOR_rl		0000 0110 ..10 00.. 0000 1101 .... ....	@b4_rd_ldmi
diff --git a/target/rx/translate.c b/target/rx/translate.c
new file mode 100644
index 0000000000..ee4ee5a760
--- /dev/null
+++ b/target/rx/translate.c
@@ -0,0 +1,2220 @@
+/*
+ *  RX translation
+ *
+ *  Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "cpu.h"
+#include "disas/disas.h"
+#include "exec/exec-all.h"
+#include "tcg-op.h"
+#include "exec/cpu_ldst.h"
+#include "exec/helper-proto.h"
+#include "exec/helper-gen.h"
+#include "exec/translator.h"
+#include "trace-tcg.h"
+#include "exec/log.h"
+
+typedef struct DisasContext {
+    DisasContextBase base;
+    CPURXState *env;
+    TCGv src;
+} DisasContext;
+
+/* Target-specific values for dc->base.is_jmp.  */
+#define DISAS_JUMP    DISAS_TARGET_0
+
+/* global register indexes */
+static TCGv cpu_regs[16];
+static TCGv cpu_psw, cpu_psw_o, cpu_psw_s, cpu_psw_z, cpu_psw_c;
+static TCGv cpu_psw_i, cpu_psw_pm, cpu_psw_u, cpu_psw_ipl;
+static TCGv cpu_usp, cpu_fpsw, cpu_bpsw, cpu_bpc, cpu_isp;
+static TCGv cpu_fintv, cpu_intb, cpu_pc;
+static TCGv_i64 cpu_acc;
+static TCGv cpu_pswop, cpu_pswop_v[3];
+
+#include "exec/gen-icount.h"
+
+static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn,
+                           int i, int n)
+{
+    while (++i <= n) {
+        uint8_t b = cpu_ldub_code(ctx->env, ctx->base.pc_next++);
+        insn |= b << (32 - i * 8);
+    }
+    return insn;
+}
+
+static uint32_t li(DisasContext *ctx, int sz)
+{
+    int32_t tmp, addr;
+    CPURXState *env = ctx->env;
+    addr = ctx->base.pc_next;
+
+    switch (sz) {
+    case 1:
+        ctx->base.pc_next += 1;
+        return cpu_ldsb_code(env, addr);
+    case 2:
+        ctx->base.pc_next += 2;
+        return cpu_ldsw_code(env, addr);
+    case 3:
+        ctx->base.pc_next += 3;
+        tmp = cpu_ldsb_code(env, addr + 2) << 16;
+        tmp |= cpu_lduw_code(env, addr) & 0xffff;
+        return tmp;
+    case 0:
+        ctx->base.pc_next += 4;
+        return cpu_ldl_code(env, addr);
+    default:
+        g_assert_not_reached();
+    }
+}
+
+/* Include the auto-generated decoder.  */
+#include "decode.inc.c"
+
+void rx_cpu_dump_state(CPUState *cs, FILE *f,
+                           fprintf_function cpu_fprintf, int flags)
+{
+    RXCPU *cpu = RXCPU(cs);
+    CPURXState *env = &cpu->env;
+    int i;
+    uint32_t psw;
+
+    psw = pack_psw(env);
+    cpu_fprintf(f, "pc=0x%08x psw=0x%08x\n",
+                env->pc, psw);
+    for (i = 0; i < 16; i += 4) {
+        cpu_fprintf(f, "r%d=0x%08x r%d=0x%08x r%d=0x%08x r%d=0x%08x\n",
+                    i, env->regs[i], i + 1, env->regs[i + 1],
+                    i + 2, env->regs[i + 2], i + 3, env->regs[i + 3]);
+    }
+}
+
+static inline void gen_save_cpu_state(DisasContext *dc, bool save_pc)
+{
+    if (save_pc) {
+        tcg_gen_movi_i32(cpu_pc, dc->base.pc_next);
+    }
+}
+
+static inline bool use_goto_tb(DisasContext *dc, target_ulong dest)
+{
+    if (unlikely(dc->base.singlestep_enabled)) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
+static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
+{
+    if (use_goto_tb(dc, dest)) {
+        tcg_gen_goto_tb(n);
+        tcg_gen_movi_i32(cpu_pc, dest);
+        tcg_gen_exit_tb(dc->base.tb, n);
+    } else {
+        tcg_gen_movi_i32(cpu_pc, dest);
+        if (dc->base.singlestep_enabled) {
+            gen_helper_debug(cpu_env);
+        } else {
+            tcg_gen_lookup_and_goto_ptr();
+        }
+    }
+    dc->base.is_jmp = DISAS_NORETURN;
+}
+
+#define RX_MEMORY_ST 0
+#define RX_MEMORY_LD 1
+#define RX_MEMORY_BYTE 0
+#define RX_MEMORY_WORD 1
+#define RX_MEMORY_LONG 2
+
+#define RX_OP_AND 0
+#define RX_OP_OR 1
+#define RX_OP_XOR 2
+#define RX_OP_TST 3
+
+#define RX_MI_BYTE 0
+#define RX_MI_WORD 1
+#define RX_MI_LONG 2
+#define RX_MI_UWORD 3
+
+/* generic load / store wrapper */
+static inline void rx_gen_ldst(unsigned int size, unsigned int dir,
+                        TCGv reg, TCGv mem)
+{
+    if (dir) {
+        tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_SIGN | MO_TE);
+    } else {
+        tcg_gen_qemu_st_i32(reg, mem, 0, size | MO_TE);
+    }
+}
+
+/* unsigned load */
+static inline void rx_gen_ldu(unsigned int size, TCGv reg, TCGv mem)
+{
+    tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_TE);
+}
+
+/* [ri, rb] */
+static inline void rx_gen_regindex(DisasContext *ctx, int size, int ri, int rb)
+{
+    tcg_gen_shli_i32(ctx->src, cpu_regs[ri], size);
+    tcg_gen_add_i32(ctx->src, ctx->src, cpu_regs[rb]);
+}
+
+/* dsp[reg] */
+static inline void rx_index_addr(int ld, int size, int reg,
+                          DisasContext *ctx)
+{
+    uint32_t dsp;
+
+    switch (ld) {
+    case 0:
+        tcg_gen_mov_i32(ctx->src, cpu_regs[reg]);
+        break;
+    case 1:
+        dsp = cpu_ldub_code(ctx->env, ctx->base.pc_next) << size;
+        tcg_gen_addi_i32(ctx->src, cpu_regs[reg], dsp);
+        ctx->base.pc_next += 1;
+        break;
+    case 2:
+        dsp = cpu_lduw_code(ctx->env, ctx->base.pc_next) << size;
+        tcg_gen_addi_i32(ctx->src, cpu_regs[reg], dsp);
+        ctx->base.pc_next += 2;
+        break;
+    }
+}
+
+/* load source operand */
+static inline TCGv rx_load_source(DisasContext *ctx, int ld, int mi, int rs)
+{
+    if (ld < 3) {
+        switch (mi) {
+        case 4:
+            /* dsp[rs].ub */
+            rx_index_addr(ld, RX_MEMORY_BYTE, rs, ctx);
+            rx_gen_ldu(RX_MEMORY_BYTE, ctx->src, ctx->src);
+            break;
+        case 3:
+            /* dsp[rs].uw */
+            rx_index_addr(ld, RX_MEMORY_WORD, rs, ctx);
+            rx_gen_ldu(RX_MEMORY_WORD, ctx->src, ctx->src);
+            break;
+        default:
+            rx_index_addr(ld, mi, rs, ctx);
+            rx_gen_ldst(mi, RX_MEMORY_LD, ctx->src, ctx->src);
+            break;
+        }
+        return ctx->src;
+    } else {
+        return cpu_regs[rs];
+    }
+}
+
+/* mov.[bwl] rs,dsp5[rd] */
+static bool trans_MOV_mr(DisasContext *ctx, arg_MOV_mr *a)
+{
+    tcg_gen_addi_i32(ctx->src, cpu_regs[a->rd], a->dsp << a->sz);
+    rx_gen_ldst(a->sz, RX_MEMORY_ST, cpu_regs[a->rs], ctx->src);
+    return true;
+}
+
+/* mov.[bwl] dsp[rd],rs */
+static bool trans_MOV_rm(DisasContext *ctx, arg_MOV_rm *a)
+{
+    tcg_gen_addi_i32(ctx->src, cpu_regs[a->rd], a->dsp << a->sz);
+    rx_gen_ldst(a->sz, RX_MEMORY_LD, cpu_regs[a->rs], ctx->src);
+    return true;
+}
+
+/* mov.l #uimm4,rd */
+/* mov.l #uimm8,rd */
+static bool trans_MOV_ri(DisasContext *ctx, arg_MOV_ri *a)
+{
+    tcg_gen_movi_i32(cpu_regs[a->rd], a->imm & 0xff);
+    return true;
+}
+
+/* mov.[bwl] #uimm8,dsp[rd] */
+static bool trans_MOV_mi(DisasContext *ctx, arg_MOV_mi *a)
+{
+    TCGv imm = tcg_const_i32(a->imm & 0xff);
+    tcg_gen_addi_i32(ctx->src, cpu_regs[a->rd], a->dsp << a->sz);
+    rx_gen_ldst(a->sz, RX_MEMORY_ST, imm, ctx->src);
+    tcg_temp_free(imm);
+    return true;
+}
+
+/* mov.l #imm,rd */
+static bool trans_MOV_rli(DisasContext *ctx, arg_MOV_rli *a)
+{
+    tcg_gen_movi_i32(cpu_regs[a->rd], a->imm);
+    return true;
+}
+
+
+/* mov #imm, dsp[rd] */
+static bool trans_MOV_mli(DisasContext *ctx, arg_MOV_mli *a)
+{
+    TCGv imm = tcg_const_i32(a->imm);
+    if (a->ld == 2) {
+        a->dsp = bswap_16(a->dsp);
+    }
+    tcg_gen_addi_i32(ctx->src, cpu_regs[a->rd], a->dsp << a->sz);
+    rx_gen_ldst(a->sz, RX_MEMORY_ST, imm, ctx->src);
+    tcg_temp_free(imm);
+    return true;
+}
+
+
+/* mov.[bwl] [ri,rb],rd */
+static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a)
+{
+    rx_gen_regindex(ctx, a->sz, a->ri, a->rb);
+    rx_gen_ldst(a->sz, RX_MEMORY_LD, cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* mov.[bwl] rd,[ri,rb] */
+static bool trans_MOV_ar(DisasContext *ctx, arg_MOV_ar *a)
+{
+    rx_gen_regindex(ctx, a->sz, a->ri, a->rb);
+    rx_gen_ldst(a->sz, RX_MEMORY_ST, cpu_regs[a->rs], ctx->src);
+    return true;
+}
+
+
+/* mov.[bwl] dsp[rs],dsp[rd] */
+/* mov.[bwl] rs,dsp[rd] */
+/* mov.[bwl] dsp[rs],rd */
+/* mov.[bwl] rs,rd */
+static bool trans_MOV_ll(DisasContext *ctx, arg_MOV_ll *a)
+{
+    int rs, rd;
+    static void (* const mov[])(TCGv ret, TCGv arg) = {
+        tcg_gen_ext8s_i32, tcg_gen_ext16s_i32, tcg_gen_mov_i32,
+    };
+    TCGv tmp;
+    if (a->lds == 3 && a->ldd < 3) {
+        rs = a->rd;
+        rd = a->rs;
+    } else {
+        rs = a->rs;
+        rd = a->rd;
+    }
+    if (a->lds == 3 && a->ldd == 3) {
+        /* mov.[bwl] rs,rd */
+        mov[a->sz](cpu_regs[rd], cpu_regs[rs]);
+    } else if (a->lds == 3) {
+        /* mov.[bwl] rs,dsp[rd] */
+        rx_index_addr(a->ldd, a->sz, rd, ctx);
+        rx_gen_ldst(a->sz, RX_MEMORY_ST, cpu_regs[rs], ctx->src);
+    } else if (a->ldd == 3) {
+        /* mov.[bwl] dsp[rs],rd */
+        rx_index_addr(a->lds, a->sz, rs, ctx);
+        rx_gen_ldst(a->sz, RX_MEMORY_LD, cpu_regs[rd], ctx->src);
+    } else {
+        /* mov.[bwl] dsp[rs],dsp[rd] */
+        tmp = tcg_temp_new();
+        rx_index_addr(a->lds, a->sz, rs, ctx);
+        rx_gen_ldst(a->sz, RX_MEMORY_LD, tmp, ctx->src);
+        rx_index_addr(a->ldd, a->sz, rd, ctx);
+        rx_gen_ldst(a->sz, RX_MEMORY_ST, tmp, ctx->src);
+        tcg_temp_free(tmp);
+    }
+    return true;
+}
+
+#define MOV_prrp(dir)                                                   \
+    do {                                                                \
+        if (a->ad == 1) {                                               \
+            tcg_gen_subi_i32(cpu_regs[a->rd],                           \
+                             cpu_regs[a->rd], 1 << a->sz);              \
+        }                                                               \
+        rx_gen_ldst(a->sz, dir, cpu_regs[a->rs], cpu_regs[a->rd]);      \
+        if (a->ad == 0) {                                               \
+            tcg_gen_addi_i32(cpu_regs[a->rd],                           \
+                             cpu_regs[a->rd], 1 << a->sz);              \
+        }                                                               \
+        return true;                                                    \
+    } while (0)
+
+/* mov.[bwl] rs,[rd+] */
+/* mov.[bwl] rs,[-rd] */
+static bool trans_MOV_pr(DisasContext *ctx, arg_MOV_pr *a)
+{
+    MOV_prrp(RX_MEMORY_ST);
+}
+
+/* mov.[bwl] [rd+],rs */
+/* mov.[bwl] [-rd],rs */
+static bool trans_MOV_rp(DisasContext *ctx, arg_MOV_rp *a)
+{
+   MOV_prrp(RX_MEMORY_LD);
+}
+
+/* movu.[bw] dsp5[rs],rd */
+static bool trans_MOVU_rm(DisasContext *ctx, arg_MOVU_rm *a)
+{
+    tcg_gen_addi_i32(ctx->src, cpu_regs[a->rs], a->dsp << a->sz);
+    rx_gen_ldu(a->sz, cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* movu.[bw] rs,rd */
+/* movu.[bw] dsp[rs],rd */
+static bool trans_MOVU_rl(DisasContext *ctx, arg_MOVU_rl *a)
+{
+    static void (* const ext[])(TCGv ret, TCGv arg) = {
+        tcg_gen_ext8u_i32, tcg_gen_ext16u_i32,
+    };
+
+    if (a->ld < 3) {
+        /* from memory */
+        rx_index_addr(a->ld, a->sz, a->rs, ctx);
+        rx_gen_ldu(a->sz, cpu_regs[a->rd], ctx->src);
+    } else {
+        ext[a->sz](cpu_regs[a->rd], cpu_regs[a->rs]);
+    }
+    return true;
+}
+
+/* movu.[bw] [ri,rb],rd */
+static bool trans_MOVU_ra(DisasContext *ctx, arg_MOVU_ra *a)
+{
+    rx_gen_regindex(ctx, a->sz, a->ri, a->rb);
+    rx_gen_ldu(a->sz, cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* movu.[bw] [rs+],rd */
+/* movu.[bw] [-rs],rd */
+static bool trans_MOVU_rp(DisasContext *ctx, arg_MOVU_rp *a)
+{
+    if (a->ad == 1) {
+        tcg_gen_subi_i32(cpu_regs[a->rs], cpu_regs[a->rs], 1 << a->sz);
+    }
+    rx_gen_ldu(a->sz, cpu_regs[a->rd], cpu_regs[a->rs]);
+    if (a->ad == 0) {
+        tcg_gen_addi_i32(cpu_regs[a->rs], cpu_regs[a->rs], 1 << a->sz);
+    }
+    return true;
+}
+
+/* pop rd */
+static bool trans_POP(DisasContext *ctx, arg_POP *a)
+{
+    rx_gen_ldst(RX_LONG, RX_MEMORY_LD, cpu_regs[a->rd], cpu_regs[0]);
+    if (a->rd != 0) {
+        tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], 4);
+    }
+    return true;
+}
+
+/* popc rx */
+static bool trans_POPC(DisasContext *ctx, arg_POPC *a)
+{
+
+    TCGv cr = tcg_const_i32(a->cr);
+    rx_gen_ldst(RX_LONG, RX_MEMORY_LD, ctx->src, cpu_regs[0]);
+    tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], 4);
+    gen_helper_mvtc(cpu_env, cr, ctx->src);
+    tcg_temp_free(cr);
+    return true;
+}
+
+/* popm rd-rd2 */
+static bool trans_POPM(DisasContext *ctx, arg_POPM *a)
+{
+    int r;
+
+    for (r = a->rd; r <= a->rd2; r++) {
+        rx_gen_ldst(RX_LONG, RX_MEMORY_LD, cpu_regs[r], cpu_regs[0]);
+        tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], 4);
+    }
+    return true;
+}
+
+/* push rs */
+static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a)
+{
+    if (a->rs != 0) {
+        tcg_gen_subi_i32(cpu_regs[0], cpu_regs[0], 4);
+        rx_gen_ldst(a->sz, RX_MEMORY_ST, cpu_regs[a->rs], cpu_regs[0]);
+    } else {
+        tcg_gen_mov_i32(ctx->src, cpu_regs[a->rs]);
+        tcg_gen_subi_i32(cpu_regs[0], cpu_regs[0], 4);
+        rx_gen_ldst(a->sz, RX_MEMORY_ST, ctx->src, cpu_regs[0]);
+    }
+    return true;
+}
+
+/* push dsp[rs] */
+static bool trans_PUSH_m(DisasContext *ctx, arg_PUSH_m *a)
+{
+    rx_index_addr(a->ld, a->sz, a->rs, ctx);
+    rx_gen_ldst(a->sz, RX_MEMORY_LD, ctx->src, ctx->src);
+    tcg_gen_subi_i32(cpu_regs[0], cpu_regs[0], 4);
+    rx_gen_ldst(a->sz, RX_MEMORY_ST, ctx->src, cpu_regs[0]);
+    return true;
+}
+
+/* pushc rx */
+static bool trans_PUSHC(DisasContext *ctx, arg_PUSHC *a)
+{
+    TCGv cr;
+    cr = tcg_const_i32(a->cr);
+    gen_helper_mvfc(ctx->src, cpu_env, cr);
+    tcg_gen_subi_i32(cpu_regs[0], cpu_regs[0], 4);
+    rx_gen_ldst(RX_LONG, RX_MEMORY_ST, ctx->src, cpu_regs[0]);
+    tcg_temp_free(cr);
+    return true;
+}
+
+/* pushm rs-rs2*/
+static bool trans_PUSHM(DisasContext *ctx, arg_PUSHM *a)
+{
+    int r;
+
+    for (r = a->rs2; r >= a->rs; r--) {
+        tcg_gen_subi_i32(cpu_regs[0], cpu_regs[0], 4);
+        rx_gen_ldst(RX_LONG, RX_MEMORY_ST, cpu_regs[r], cpu_regs[0]);
+    }
+    return true;
+}
+
+/* xchg rs,rd */
+/* xchg dsp[rs].<mi>,rd */
+static bool trans_XCHG_rl(DisasContext *ctx, arg_XCHG_rl *a)
+{
+    int sz;
+    TCGv tmp;
+    tmp = tcg_temp_new();
+    if (a->ld == 3) {
+        /* xchg rs,rd */
+        tcg_gen_mov_i32(tmp, cpu_regs[a->rs]);
+        tcg_gen_mov_i32(cpu_regs[a->rs], cpu_regs[a->rd]);
+        tcg_gen_mov_i32(cpu_regs[a->rd], tmp);
+    } else {
+        switch (a->mi) {
+        case 0 ... 2:
+            rx_index_addr(a->ld, a->mi, a->rs, ctx);
+            rx_gen_ldst(a->mi, RX_MEMORY_LD, tmp, ctx->src);
+            sz = a->mi;
+            break;
+        case 3:
+            rx_index_addr(a->ld, RX_MEMORY_WORD, a->rs, ctx);
+            rx_gen_ldu(RX_MEMORY_WORD, tmp, ctx->src);
+            sz = RX_MEMORY_WORD;
+            break;
+        case 4:
+            rx_index_addr(a->ld, RX_MEMORY_BYTE, a->rs, ctx);
+            rx_gen_ldu(RX_MEMORY_BYTE, tmp, ctx->src);
+            sz = RX_MEMORY_BYTE;
+            break;
+        }
+        rx_gen_ldst(sz, RX_MEMORY_ST, cpu_regs[a->rd], ctx->src);
+        tcg_gen_mov_i32(cpu_regs[a->rd], tmp);
+    }
+    tcg_temp_free(tmp);
+    return true;
+}
+
+#define STZFN(maskop)                                                   \
+    do {                                                                \
+        TCGv mask, imm;                                                 \
+        mask = tcg_temp_new();                                          \
+        imm = tcg_temp_new();                                           \
+        maskop;                                                         \
+        tcg_gen_andi_i32(imm, mask, a->imm);                            \
+        tcg_gen_andc_i32(cpu_regs[a->rd], cpu_regs[a->rd], mask);       \
+        tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], imm);          \
+        tcg_temp_free(mask);                                            \
+        tcg_temp_free(imm);                                             \
+        return true;                                                    \
+    } while (0)
+
+/* stz #imm,rd */
+static bool trans_STZ(DisasContext *ctx, arg_STZ *a)
+{
+    STZFN(tcg_gen_neg_i32(mask, cpu_psw_z));
+}
+
+/* stnz #imm,rd */
+static bool trans_STNZ(DisasContext *ctx, arg_STNZ *a)
+{
+    STZFN(tcg_gen_subi_i32(mask, cpu_psw_z, 1));
+}
+
+/* sccnd.[bwl] rd */
+/* sccnd.[bwl] dsp:[rd] */
+static bool trans_SCCnd(DisasContext *ctx, arg_SCCnd *a)
+{
+    TCGv cond = tcg_const_i32(a->cd);
+    TCGv val;
+    val = tcg_temp_new();
+    gen_helper_sccond(val, cpu_env, cond);
+    if (a->ld < 3) {
+        rx_index_addr(a->sz, a->ld, a->rd, ctx);
+        rx_gen_ldst(a->sz, RX_MEMORY_ST, val, ctx->src);
+    } else {
+        tcg_gen_mov_i32(cpu_regs[a->rd], val);
+    }
+    tcg_temp_free(cond);
+    tcg_temp_free(val);
+    return true;
+}
+
+/* rtsd #imm */
+static bool trans_RTSD_i(DisasContext *ctx, arg_RTSD_i *a)
+{
+    tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], (a->imm & 0xff)  << 2);
+    rx_gen_ldst(RX_LONG, RX_MEMORY_LD, cpu_pc, cpu_regs[0]);
+    tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], 4);
+    ctx->base.is_jmp = DISAS_JUMP;
+    return true;
+}
+
+/* rtsd #imm, rd-rd2 */
+static bool trans_RTSD_irr(DisasContext *ctx, arg_RTSD_irr *a)
+{
+    int dst;
+    int adj = (a->imm & 0xff) - (a->rd2 - a->rd + 1);
+
+    tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], adj << 2);
+    for (dst = a->rd; dst <= a->rd2; dst++) {
+        rx_gen_ldst(RX_LONG, RX_MEMORY_LD, cpu_regs[dst], cpu_regs[0]);
+        tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], 4);
+    }
+    rx_gen_ldst(RX_LONG, RX_MEMORY_LD, cpu_pc, cpu_regs[0]);
+    tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], 4);
+    ctx->base.is_jmp = DISAS_JUMP;
+    return true;
+}
+
+#define GEN_LOGIC_OP(opr, ret, arg1, arg2) \
+    do {                                                                \
+        opr(ret, arg1, arg2);                                       \
+        tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, ret, 0);           \
+        tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s, ret, 0x80000000UL); \
+    } while (0)
+
+/* and #uimm:4, rd */
+static bool trans_AND_ri(DisasContext *ctx, arg_AND_ri *a)
+{
+    GEN_LOGIC_OP(tcg_gen_andi_i32, cpu_regs[a->rd], cpu_regs[a->rd], a->imm);
+    return true;
+}
+
+/* and #imm, rd */
+static bool trans_AND_rli(DisasContext *ctx, arg_AND_rli *a)
+{
+    GEN_LOGIC_OP(tcg_gen_andi_i32, cpu_regs[a->rd], cpu_regs[a->rd], a->imm);
+    return true;
+}
+
+/* and dsp[rs], rd */
+/* and rs,rd */
+static bool trans_AND_rl(DisasContext *ctx, arg_AND_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    GEN_LOGIC_OP(tcg_gen_and_i32, cpu_regs[a->rd], cpu_regs[a->rd], val);
+    return true;
+}
+
+/* and rs,rs2,rd */
+static bool trans_AND_rrr(DisasContext *ctx, arg_AND_rrr *a)
+{
+    GEN_LOGIC_OP(tcg_gen_and_i32, cpu_regs[a->rd],
+                 cpu_regs[a->rs2], cpu_regs[a->rs]);
+    return true;
+}
+
+/* or #uimm:4, rd */
+static bool trans_OR_ri(DisasContext *ctx, arg_OR_ri *a)
+{
+    GEN_LOGIC_OP(tcg_gen_ori_i32, cpu_regs[a->rd], cpu_regs[a->rd], a->imm);
+    return true;
+}
+
+/* or #imm, rd */
+static bool trans_OR_rli(DisasContext *ctx, arg_OR_rli *a)
+{
+    GEN_LOGIC_OP(tcg_gen_ori_i32, cpu_regs[a->rd], cpu_regs[a->rd], a->imm);
+    return true;
+}
+
+/* or dsp[rs], rd */
+/* or rs,rd */
+static bool trans_OR_rl(DisasContext *ctx, arg_OR_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    GEN_LOGIC_OP(tcg_gen_or_i32, cpu_regs[a->rd], cpu_regs[a->rd], val);
+    return true;
+}
+
+/* or rs,rs2,rd */
+static bool trans_OR_rrr(DisasContext *ctx, arg_OR_rrr *a)
+{
+    GEN_LOGIC_OP(tcg_gen_or_i32, cpu_regs[a->rd],
+                 cpu_regs[a->rs2], cpu_regs[a->rs]);
+    return true;
+}
+
+/* xor #imm, rd */
+static bool trans_XOR_rli(DisasContext *ctx, arg_XOR_rli *a)
+{
+    GEN_LOGIC_OP(tcg_gen_xori_i32, cpu_regs[a->rd], cpu_regs[a->rd], a->imm);
+    return true;
+}
+
+/* xor dsp[rs], rd */
+/* xor rs,rd */
+static bool trans_XOR_rl(DisasContext *ctx, arg_XOR_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    GEN_LOGIC_OP(tcg_gen_xor_i32, cpu_regs[a->rd], cpu_regs[a->rd], val);
+    return true;
+}
+
+/* tst #imm, rd */
+static bool trans_TST_rli(DisasContext *ctx, arg_TST_rli *a)
+{
+    TCGv tmp;
+    tmp = tcg_temp_new();
+    GEN_LOGIC_OP(tcg_gen_andi_i32, tmp, cpu_regs[a->rd], a->imm);
+    tcg_temp_free(tmp);
+    return true;
+}
+
+/* tst dsp[rs], rd */
+/* tst rs, rd */
+static bool trans_TST_rl(DisasContext *ctx, arg_TST_rl *a)
+{
+    TCGv val;
+    TCGv tmp;
+    tmp = tcg_temp_new();
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    GEN_LOGIC_OP(tcg_gen_and_i32, tmp, cpu_regs[a->rd], val);
+    tcg_temp_free(tmp);
+    return true;
+}
+
+/* not rd */
+/* not rs, rd */
+static bool trans_NOT_rr(DisasContext *ctx, arg_NOT_rr *a)
+{
+    int rs;
+    rs = a->rs < 16 ? a->rs : a->rd;
+    tcg_gen_not_i32(cpu_regs[a->rd], cpu_regs[rs]);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, cpu_regs[a->rd], 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s,
+                         cpu_regs[a->rd], 0x80000000UL);
+    return true;
+}
+
+/* neg rd */
+/* neg rs, rd */
+static bool trans_NEG_rr(DisasContext *ctx, arg_NEG_rr *a)
+{
+    int rs;
+    rs = a->rs < 16 ? a->rs : a->rd;
+    tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_NONE);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_o, cpu_regs[rs], 0x80000000);
+    tcg_gen_neg_i32(cpu_regs[a->rd], cpu_regs[rs]);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, cpu_regs[a->rd], 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s,
+                         cpu_regs[a->rd], 0x80000000UL);
+    tcg_gen_mov_i32(cpu_psw_c, cpu_psw_z);
+    return true;
+}
+
+/* ret = arg1 + arg2 + psw_c */
+static void rx_gen_adc_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv cf, z;
+    cf = tcg_temp_new();
+    z = tcg_const_i32(0);
+    tcg_gen_mov_i32(cf, cpu_psw_c);
+    tcg_gen_add2_i32(ret, cpu_psw_c, arg1, z, arg2, z);
+    tcg_gen_add2_i32(ret, cpu_psw_c, ret, cpu_psw_c, cf, z);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, ret, 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s, ret, 0x80000000UL);
+    tcg_gen_mov_i32(cpu_pswop_v[0], arg1);
+    tcg_gen_mov_i32(cpu_pswop_v[1], arg2);
+    tcg_gen_mov_i32(cpu_pswop_v[2], ret);
+    tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_ADD);
+    tcg_temp_free(cf);
+    tcg_temp_free(z);
+}
+
+/* adc #imm, rd */
+static bool trans_ADC_rli(DisasContext *ctx, arg_ADC_rli *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    rx_gen_adc_i32(cpu_regs[a->rd], cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* adc rs, rd */
+static bool trans_ADC_rr(DisasContext *ctx, arg_ADC_rr *a)
+{
+    rx_gen_adc_i32(cpu_regs[a->rd], cpu_regs[a->rd], cpu_regs[a->rs]);
+    return true;
+}
+
+/* adc dsp[rs], rd */
+static bool trans_ADC_rl(DisasContext *ctx, arg_ADC_rl *a)
+{
+    rx_index_addr(a->ld, RX_LONG, a->rs, ctx);
+    rx_gen_ldst(a->ld, RX_MEMORY_LD, ctx->src, ctx->src);
+    rx_gen_adc_i32(cpu_regs[a->rd], cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* ret = arg1 + arg2 */
+static void rx_gen_add_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv z;
+    z = tcg_const_i32(0);
+    tcg_gen_add2_i32(ret, cpu_psw_c, arg1, z, arg2, z);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, ret, 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s, ret, 0x80000000UL);
+    tcg_gen_mov_i32(cpu_pswop_v[0], arg1);
+    tcg_gen_mov_i32(cpu_pswop_v[1], arg2);
+    tcg_gen_mov_i32(cpu_pswop_v[2], ret);
+    tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_ADD);
+    tcg_temp_free(z);
+}
+
+/* add #uimm4, rd */
+static bool trans_ADD_rri(DisasContext *ctx, arg_ADD_rri *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    rx_gen_add_i32(cpu_regs[a->rd], cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* add rs, rd */
+/* add dsp[rs], rd */
+static bool trans_ADD_rl(DisasContext *ctx, arg_ADD_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    rx_gen_add_i32(cpu_regs[a->rd], cpu_regs[a->rd], val);
+    return true;
+}
+
+/* add #imm, rs, rd */
+static bool trans_ADD_rrli(DisasContext *ctx, arg_ADD_rrli *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    rx_gen_add_i32(cpu_regs[a->rd], cpu_regs[a->rs2], ctx->src);
+    return true;
+}
+
+/* add rs, rs2, rd */
+static bool trans_ADD_rrr(DisasContext *ctx, arg_ADD_rrr *a)
+{
+    rx_gen_add_i32(cpu_regs[a->rd], cpu_regs[a->rs], cpu_regs[a->rs2]);
+    return true;
+}
+
+/* ret = arg1 - arg2 */
+static void rx_gen_sub_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv z;
+    z = tcg_const_i32(0);
+    tcg_gen_sub2_i32(ret, cpu_psw_c, arg1, z, arg2, z);
+    tcg_gen_addi_i32(cpu_psw_c, cpu_psw_c, 1);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, ret, 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s, ret, 0x80000000UL);
+    tcg_gen_mov_i32(cpu_pswop_v[0], arg1);
+    tcg_gen_mov_i32(cpu_pswop_v[1], arg2);
+    tcg_gen_mov_i32(cpu_pswop_v[2], ret);
+    tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_SUB);
+    tcg_temp_free(z);
+}
+
+/* ret = arg1 - arg2 - !psw_c */
+static void rx_gen_sbb_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv cf, z;
+    cf = tcg_temp_new();
+    z = tcg_const_i32(0);
+    tcg_gen_xori_i32(cf, cpu_psw_c, 1);
+    tcg_gen_sub2_i32(ret, cpu_psw_c, arg1, z, arg2, z);
+    tcg_gen_sub2_i32(ret, cpu_psw_c, ret, cpu_psw_c, cf, z);
+    tcg_gen_addi_i32(cpu_psw_c, cpu_psw_c, 1);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, ret, 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s, ret, 0x80000000UL);
+    tcg_gen_mov_i32(cpu_pswop_v[0], arg1);
+    tcg_gen_mov_i32(cpu_pswop_v[1], arg2);
+    tcg_gen_mov_i32(cpu_pswop_v[2], ret);
+    tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_SUB);
+    tcg_temp_free(cf);
+    tcg_temp_free(z);
+}
+
+/* cmp #imm4, rd */
+/* cmp #imm8, rd */
+static bool trans_CMP_ri(DisasContext *ctx, arg_CMP_ri *a)
+{
+    int rs;
+    TCGv tmp;
+    tmp = tcg_temp_new();
+    rs = (a->rs2 < 16) ? a->rs2 : a->rd;
+    tcg_gen_movi_i32(ctx->src, a->imm & 0xff);
+    rx_gen_sub_i32(tmp,  cpu_regs[rs], ctx->src);
+    tcg_temp_free(tmp);
+    return true;
+}
+
+/* cmp #imm, rs2 */
+static bool trans_CMP_rli(DisasContext *ctx, arg_CMP_rli *a)
+{
+    TCGv tmp;
+    tmp = tcg_temp_new();
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    rx_gen_sub_i32(tmp, cpu_regs[a->rs2], ctx->src);
+    tcg_temp_free(tmp);
+    return true;
+}
+
+/* cmp rs, rs2 */
+/* cmp dsp[rs], rs2 */
+static bool trans_CMP_rl(DisasContext *ctx, arg_CMP_rl *a)
+{
+    TCGv val;
+    TCGv tmp;
+    tmp = tcg_temp_new();
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    rx_gen_sub_i32(tmp, cpu_regs[a->rd], val);
+    tcg_temp_free(tmp);
+    return true;
+}
+
+/* sub #imm4, rd */
+static bool trans_SUB_ri(DisasContext *ctx, arg_SUB_ri *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    rx_gen_sub_i32(cpu_regs[a->rd], cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* sub rs, rd */
+/* sub dsp[rs], rd */
+static bool trans_SUB_rl(DisasContext *ctx, arg_SUB_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    rx_gen_sub_i32(cpu_regs[a->rd], cpu_regs[a->rd], val);
+    return true;
+}
+
+/* sub rs, rs2, rd */
+static bool trans_SUB_rrr(DisasContext *ctx, arg_SUB_rrr *a)
+{
+    rx_gen_sub_i32(cpu_regs[a->rd], cpu_regs[a->rs2], cpu_regs[a->rs]);
+    return true;
+}
+
+/* sbb rs, rd */
+static bool trans_SBB_rr(DisasContext *ctx, arg_SBB_rr *a)
+{
+    rx_gen_sbb_i32(cpu_regs[a->rd], cpu_regs[a->rd], cpu_regs[a->rs]);
+    return true;
+}
+
+/* sbb dsp[rs], rd */
+static bool trans_SBB_rl(DisasContext *ctx, arg_SBB_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, RX_MI_LONG, a->rs);
+    rx_gen_sbb_i32(cpu_regs[a->rd], cpu_regs[a->rd], val);
+    return true;
+}
+
+/* abs rd */
+/* abs rs, rd */
+static bool trans_ABS_rr(DisasContext *ctx, arg_ABS_rr *a)
+{
+    TCGv neg;
+    TCGv zero;
+    int rs;
+    rs = a->rs < 16 ? a->rs : a->rd;
+    neg = tcg_temp_new();
+    zero = tcg_const_i32(0);
+    tcg_gen_neg_i32(neg, cpu_regs[rs]);
+    tcg_gen_movcond_i32(TCG_COND_LT, cpu_regs[a->rd], cpu_regs[rs], zero,
+                        neg, cpu_regs[rs]);
+    tcg_temp_free(neg);
+    tcg_temp_free(zero);
+    return true;
+}
+
+/* max #imm, rd */
+static bool trans_MAX_ri(DisasContext *ctx, arg_MAX_ri *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    tcg_gen_smax_i32(cpu_regs[a->rd], cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* max rs, rd */
+/* max dsp[rs], rd */
+static bool trans_MAX_rl(DisasContext *ctx, arg_MAX_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    tcg_gen_smax_i32(cpu_regs[a->rd], cpu_regs[a->rd], val);
+    return true;
+}
+
+/* min #imm, rd */
+static bool trans_MIN_ri(DisasContext *ctx, arg_MIN_ri *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    tcg_gen_smin_i32(cpu_regs[a->rd], cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* min rs, rd */
+/* min dsp[rs], rd */
+static bool trans_MIN_rl(DisasContext *ctx, arg_MIN_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    tcg_gen_smin_i32(cpu_regs[a->rd], cpu_regs[a->rd], val);
+    return true;
+}
+
+/* mul #uimm4, rd */
+static bool trans_MUL_ri(DisasContext *ctx, arg_MUL_ri *a)
+{
+    tcg_gen_muli_i32(cpu_regs[a->rd], cpu_regs[a->rd], a->imm);
+    return true;
+}
+
+/* mul #imm, rd */
+static bool trans_MUL_rli(DisasContext *ctx, arg_MUL_rli *a)
+{
+    tcg_gen_muli_i32(cpu_regs[a->rd], cpu_regs[a->rd], a->imm);
+    return true;
+}
+
+/* mul rs, rd */
+/* mul dsp[rs], rd */
+static bool trans_MUL_rl(DisasContext *ctx, arg_MUL_rl *a)
+{
+    TCGv val;
+
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    tcg_gen_mul_i32(cpu_regs[a->rd], cpu_regs[a->rd], val);
+    return true;
+}
+
+/* mul rs, rs2, rd */
+static bool trans_MUL_rrr(DisasContext *ctx, arg_MUL_rrr *a)
+{
+    tcg_gen_mul_i32(cpu_regs[a->rd], cpu_regs[a->rs], cpu_regs[a->rs2]);
+    return true;
+}
+
+/* emul #imm, rd */
+static bool trans_EMUL_ri(DisasContext *ctx, arg_EMUL_ri *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    tcg_gen_muls2_i32(cpu_regs[a->rd], cpu_regs[a->rd + 1],
+                      cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* emul rs, rd */
+/* emul dsp[rs], rd */
+static bool trans_EMUL_rl(DisasContext *ctx, arg_EMUL_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    tcg_gen_muls2_i32(cpu_regs[a->rd], cpu_regs[a->rd + 1],
+                      cpu_regs[a->rd], val);
+    return true;
+}
+
+/* emulu #imm, rd */
+static bool trans_EMULU_ri(DisasContext *ctx, arg_EMULU_ri *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    tcg_gen_mulu2_i32(cpu_regs[a->rd], cpu_regs[a->rd + 1],
+                      cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* emulu rs, rd */
+/* emulu dsp[rs], rd */
+static bool trans_EMULU_rl(DisasContext *ctx, arg_EMULU_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    tcg_gen_mulu2_i32(cpu_regs[a->rd], cpu_regs[a->rd + 1],
+                      cpu_regs[a->rd], val);
+    return true;
+}
+
+/* div #imm, rd */
+static bool trans_DIV_ri(DisasContext *ctx, arg_DIV_ri *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    gen_helper_div(cpu_regs[a->rd], cpu_env, cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* div rs, rd */
+/* div dsp[rs], rd */
+static bool trans_DIV_rl(DisasContext *ctx, arg_DIV_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    gen_helper_divu(cpu_regs[a->rd], cpu_env, cpu_regs[a->rd], val);
+    return true;
+}
+
+/* divu #imm, rd */
+static bool trans_DIVU_ri(DisasContext *ctx, arg_DIVU_ri *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm);
+    gen_helper_divu(cpu_regs[a->rd], cpu_env, cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* divu rs, rd */
+/* divu dsp[rs], rd */
+static bool trans_DIVU_rl(DisasContext *ctx, arg_DIVU_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    gen_helper_divu(cpu_regs[a->rd], cpu_env, cpu_regs[a->rd], val);
+    return true;
+}
+
+
+/* shll #imm:5, rd */
+/* shll #imm:5, rs, rd */
+static bool trans_SHLL_rri(DisasContext *ctx, arg_SHLL_rri *a)
+{
+    int rs;
+
+    rs =  (a->rs2 >= 16) ? a->rd : a->rs2;
+    if (a->imm) {
+        tcg_gen_shri_i32(cpu_psw_c, cpu_regs[a->rd], 32 - a->imm);
+        tcg_gen_shli_i32(cpu_regs[a->rd], cpu_regs[rs], a->imm);
+        tcg_gen_mov_i32(cpu_pswop_v[0], cpu_regs[rs]);
+        tcg_gen_movi_i32(cpu_pswop_v[0], a->imm);
+        tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_SHLL);
+    } else {
+        tcg_gen_movi_i32(cpu_psw_c, 0);
+        tcg_gen_movi_i32(cpu_psw_o, 0);
+        tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_NONE);
+    }
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, cpu_regs[a->rd], 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s,
+                         cpu_regs[a->rd], 0x80000000UL);
+    return true;
+}
+
+/* shll rs, rd */
+static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a)
+{
+    TCGLabel *l1, *l2;
+    TCGv count;
+
+    l1 = gen_new_label();
+    l2 = gen_new_label();
+    tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_regs[a->rs], 0, l1);
+    count = tcg_const_i32(32);
+    tcg_gen_sub_i32(ctx->src, count, cpu_regs[a->rs]);
+    tcg_gen_shr_i32(cpu_psw_c, cpu_regs[a->rd], count);
+    tcg_gen_shl_i32(cpu_regs[a->rd], cpu_regs[a->rd], cpu_regs[a->rs]);
+    tcg_gen_mov_i32(cpu_pswop_v[0], cpu_regs[a->rd]);
+    tcg_gen_mov_i32(cpu_pswop_v[1], cpu_regs[a->rs]);
+    tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_SHLL);
+    tcg_gen_br(l2);
+    gen_set_label(l1);
+    tcg_gen_movi_i32(cpu_psw_c, 0);
+    tcg_gen_movi_i32(cpu_psw_o, 0);
+    tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_NONE);
+    gen_set_label(l2);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, cpu_regs[a->rd], 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s,
+                         cpu_regs[a->rd], 0x80000000UL);
+    tcg_temp_free(count);
+    return true;
+}
+
+#define SHIFTR_IMM(op)                                                  \
+    do {                                                                \
+        int rs;                                                         \
+        rs =  (a->rs2 >= 16) ? a->rd : a->rs2;                          \
+        if (a->imm) {                                                   \
+            op(cpu_regs[a->rd], cpu_regs[rs], a->imm - 1);              \
+            tcg_gen_andi_i32(cpu_psw_c, cpu_regs[a->rd], 0x00000001);   \
+            op(cpu_regs[a->rd], cpu_regs[a->rd], 1);                    \
+        } else {                                                        \
+            tcg_gen_movi_i32(cpu_psw_c, 0);                             \
+        }                                                               \
+        tcg_gen_movi_i32(cpu_psw_o, 0);                                 \
+        tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_NONE);                    \
+        tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, cpu_regs[a->rd], 0); \
+        tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s,                   \
+                             cpu_regs[a->rd], 0x80000000UL);            \
+    } while (0)
+
+#define SHIFTR_REG(op, opimm)                                                 \
+    do {                                                                \
+        TCGLabel *skipz, *done;                                         \
+        TCGv count;                                                     \
+        skipz = gen_new_label();                                        \
+        done = gen_new_label();                                         \
+        count = tcg_temp_new();                                         \
+        tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_regs[a->rs], 0, skipz);    \
+        tcg_gen_subi_i32(count, cpu_regs[a->rs], 1);                    \
+        op(cpu_regs[a->rd], cpu_regs[a->rd], count);                    \
+        tcg_gen_andi_i32(cpu_psw_c, cpu_regs[a->rd], 0x00000001);       \
+        opimm(cpu_regs[a->rd], cpu_regs[a->rd], 1);                     \
+        tcg_gen_br(done);                                               \
+        gen_set_label(skipz);                                           \
+        tcg_gen_movi_i32(cpu_psw_c, 0);                                 \
+        gen_set_label(done);                                            \
+        tcg_gen_movi_i32(cpu_psw_o, 0);                                 \
+        tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_NONE);                    \
+        tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, cpu_regs[a->rd], 0); \
+        tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s,                   \
+                             cpu_regs[a->rd], 0x80000000UL);            \
+        tcg_temp_free(count);                                           \
+    } while (0)
+
+/* shar #imm:5, rd */
+/* shar #imm:5, rs, rd */
+static bool trans_SHAR_rri(DisasContext *ctx, arg_SHAR_rri *a)
+{
+    SHIFTR_IMM(tcg_gen_sari_i32);
+    return true;
+}
+
+/* shar rs, rd */
+static bool trans_SHAR_rr(DisasContext *ctx, arg_SHAR_rr *a)
+{
+    SHIFTR_REG(tcg_gen_sar_i32, tcg_gen_sari_i32);
+    return true;
+}
+
+/* shlr #imm:5, rd */
+/* shlr #imm:5, rs, rd */
+static bool trans_SHLR_rri(DisasContext *ctx, arg_SHLR_rri *a)
+{
+    SHIFTR_IMM(tcg_gen_shri_i32);
+    return true;
+}
+
+/* shlr rs, rd */
+static bool trans_SHLR_rr(DisasContext *ctx, arg_SHLR_rr *a)
+{
+    SHIFTR_REG(tcg_gen_shr_i32, tcg_gen_shri_i32);
+    return true;
+}
+
+/* rolc rd*/
+static bool trans_ROLC(DisasContext *ctx, arg_ROLC *a)
+{
+    TCGv tmp;
+    tmp = tcg_temp_new();
+    tcg_gen_shri_i32(tmp, cpu_regs[a->rd], 31);
+    tcg_gen_shli_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1);
+    tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], cpu_psw_c);
+    tcg_gen_mov_i32(cpu_psw_c, tmp);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, cpu_regs[a->rd], 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s,
+                         cpu_regs[a->rd], 0x80000000UL);
+    tcg_temp_free(tmp);
+    return true;
+}
+
+/* rorc rd */
+static bool trans_RORC(DisasContext *ctx, arg_RORC *a)
+{
+    TCGv tmp;
+    tmp = tcg_temp_new();
+    tcg_gen_andi_i32(tmp, cpu_regs[a->rd], 0x00000001);
+    tcg_gen_shri_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1);
+    tcg_gen_shli_i32(cpu_psw_c, cpu_psw_c, 31);
+    tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], cpu_psw_c);
+    tcg_gen_mov_i32(cpu_psw_c, tmp);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, cpu_regs[a->rd], 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s,
+                         cpu_regs[a->rd], 0x80000000UL);
+    return true;
+}
+
+
+static void rx_rot_imm(int dir, int rd, uint32_t imm)
+{
+    if (dir) {
+        tcg_gen_rotli_i32(cpu_regs[rd], cpu_regs[rd], imm);
+        tcg_gen_andi_i32(cpu_psw_c, cpu_regs[rd], 0x00000001);
+    } else {
+        tcg_gen_rotri_i32(cpu_regs[rd], cpu_regs[rd], imm);
+        tcg_gen_shri_i32(cpu_psw_c, cpu_regs[rd], 31);
+    }
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, cpu_regs[rd], 0);
+    tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s, cpu_regs[rd], 0x80000000UL);
+}
+
+static void rx_rot_reg(int dir, int rd, int rs)
+{
+    if (dir) {
+        tcg_gen_rotl_i32(cpu_regs[rd], cpu_regs[rd], cpu_regs[rs]);
+        tcg_gen_andi_i32(cpu_psw_c, cpu_regs[rd], 0x00000001);
+    } else {
+        tcg_gen_rotr_i32(cpu_regs[rd], cpu_regs[rd], cpu_regs[rs]);
+        tcg_gen_shri_i32(cpu_psw_c, cpu_regs[rd], 31);
+    }
+}
+
+/* rotl #imm, rd */
+static bool trans_ROTL_ri(DisasContext *ctx, arg_ROTL_ri *a)
+{
+    rx_rot_imm(1, a->rd, a->imm);
+    return true;
+}
+
+/* rotl rs, rd */
+static bool trans_ROTL_rr(DisasContext *ctx, arg_ROTL_rr *a)
+{
+    rx_rot_reg(1, a->rd, a->rs);
+    return true;
+}
+
+/* rotr #imm, rd */
+static bool trans_ROTR_ri(DisasContext *ctx, arg_ROTR_ri *a)
+{
+    rx_rot_imm(0, a->rd, a->imm);
+    return true;
+}
+
+/* rotr rs, rd */
+static bool trans_ROTR_rr(DisasContext *ctx, arg_ROTR_rr *a)
+{
+    rx_rot_reg(0, a->rd, a->rs);
+    return true;
+}
+
+/* revl rs, rd */
+static bool trans_REVL(DisasContext *ctx, arg_REVL *a)
+{
+    tcg_gen_bswap32_i32(cpu_regs[a->rd], cpu_regs[a->rs]);
+    return true;
+}
+
+/* revw rs, rd */
+static bool trans_REVW(DisasContext *ctx, arg_REVW *a)
+{
+    TCGv hi, lo;
+
+    hi = tcg_temp_new();
+    lo = tcg_temp_new();
+    tcg_gen_shri_i32(hi, cpu_regs[a->rs], 16);
+    tcg_gen_bswap16_i32(hi, hi);
+    tcg_gen_shli_i32(hi, hi, 16);
+    tcg_gen_bswap16_i32(lo, cpu_regs[a->rs]);
+    tcg_gen_or_i32(cpu_regs[a->rd], hi, lo);
+    tcg_temp_free(hi);
+    tcg_temp_free(lo);
+    return true;
+}
+
+/* conditional branch helper */
+static void rx_bcnd_main(DisasContext *ctx, int cd, int dst, int len)
+{
+        TCGv t, f, cond;
+        switch (cd) {
+        case 0 ... 13:
+            t = tcg_const_i32(ctx->base.pc_next - len + dst);
+            f = tcg_const_i32(ctx->base.pc_next);
+            cond = tcg_const_i32(cd);
+            gen_helper_brcond(cpu_pc, cpu_env, cond, t, f);
+            tcg_temp_free(cond);
+            tcg_temp_free(t);
+            tcg_temp_free(f);
+            break;
+        case 14:
+            /* always true case */
+            tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next - len + dst);
+            break;
+        case 15:
+            /* always false case */
+            tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next);
+            break;
+    }
+    ctx->base.is_jmp = DISAS_JUMP;
+}
+
+static int16_t rev16(uint16_t dsp)
+{
+    return ((dsp << 8) & 0xff00) | ((dsp >> 8) & 0x00ff);
+}
+
+static int32_t rev24(uint32_t dsp)
+{
+    dsp = ((dsp << 16) & 0xff0000) |
+        (dsp & 0x00ff00) |
+        ((dsp >> 16) & 0x0000ff);
+    dsp |= (dsp & 0x00800000) ? 0xff000000 : 0x00000000;
+    return dsp;
+}
+
+/* beq dsp:3 */
+/* bne dsp:3 */
+static bool trans_BCnd_s(DisasContext *ctx, arg_BCnd_s *a)
+{
+    if (a->dsp < 3) {
+        a->dsp += 8;
+    }
+    rx_bcnd_main(ctx, a->cd, a->dsp, 1);
+    return true;
+}
+
+/* beq dsp:8 */
+/* bne dsp:8 */
+/* bc dsp:8 */
+/* bnc dsp:8 */
+/* bgtu dsp:8 */
+/* bleu dsp:8 */
+/* bpz dsp:8 */
+/* bn dsp:8 */
+/* bge dsp:8 */
+/* blt dsp:8 */
+/* bgt dsp:8 */
+/* ble dsp:8 */
+/* bo dsp:8 */
+/* bno dsp:8 */
+/* bra dsp:8 */
+static bool trans_BCnd_b(DisasContext *ctx, arg_BCnd_b *a)
+{
+    rx_bcnd_main(ctx, a->cd, (int8_t)a->dsp, 2);
+    return true;
+}
+
+/* beq dsp:16 */
+/* bne dsp:16 */
+static bool trans_BCnd_w(DisasContext *ctx, arg_BCnd_w *a)
+{
+    rx_bcnd_main(ctx, a->cd, rev16(a->dsp), 3);
+    return true;
+}
+
+/* bra dsp:3 */
+static bool trans_BRA_s(DisasContext *ctx, arg_BRA_s *a)
+{
+    if (a->dsp < 3) {
+        a->dsp += 8;
+    }
+    rx_bcnd_main(ctx, 14, a->dsp, 1);
+    return true;
+}
+
+/* bra dsp:16 */
+static bool trans_BRA_w(DisasContext *ctx, arg_BRA_w *a)
+{
+    rx_bcnd_main(ctx, 14, rev16(a->dsp), 3);
+    return true;
+}
+
+/* bra dsp:24 */
+static bool trans_BRA_a(DisasContext *ctx, arg_BRA_a *a)
+{
+    rx_bcnd_main(ctx, 14, rev24(a->dsp), 4);
+    return true;
+}
+
+/* bra rs */
+static bool trans_BRA_l(DisasContext *ctx, arg_BRA_l *a)
+{
+    tcg_gen_add_i32(cpu_pc, cpu_pc, cpu_regs[a->rd]);
+    ctx->base.is_jmp = DISAS_JUMP;
+    return true;
+}
+
+static void rx_save_pc(DisasContext *ctx)
+{
+    tcg_gen_movi_i32(ctx->src, ctx->base.pc_next);
+    tcg_gen_subi_i32(cpu_regs[0], cpu_regs[0], 4);
+    rx_gen_ldst(RX_LONG, RX_MEMORY_ST, ctx->src, cpu_regs[0]);
+}
+
+/* jmp rs */
+static bool trans_JMP(DisasContext *ctx, arg_JMP *a)
+{
+    tcg_gen_mov_i32(cpu_pc, cpu_regs[a->rs]);
+    ctx->base.is_jmp = DISAS_JUMP;
+    return true;
+}
+
+/* jsr rs */
+static bool trans_JSR(DisasContext *ctx, arg_JSR *a)
+{
+    rx_save_pc(ctx);
+    tcg_gen_mov_i32(cpu_pc, cpu_regs[a->rs]);
+    ctx->base.is_jmp = DISAS_JUMP;
+    return true;
+}
+
+/* bsr dsp:16 */
+static bool trans_BSR_w(DisasContext *ctx, arg_BSR_w *a)
+{
+    rx_save_pc(ctx);
+    rx_bcnd_main(ctx, 14, rev16(a->dsp), 3);
+    return true;
+}
+
+/* bsr dsp:24 */
+static bool trans_BSR_a(DisasContext *ctx, arg_BSR_a *a)
+{
+    rx_save_pc(ctx);
+    rx_bcnd_main(ctx, 14, rev24(a->dsp), 4);
+    return true;
+}
+
+/* bsr rs */
+static bool trans_BSR_l(DisasContext *ctx, arg_BSR_l *a)
+{
+    rx_save_pc(ctx);
+    tcg_gen_add_i32(cpu_pc, cpu_pc, cpu_regs[a->rd]);
+    ctx->base.is_jmp = DISAS_JUMP;
+    return true;
+}
+
+/* rts */
+static bool trans_RTS(DisasContext *ctx, arg_RTS *a)
+{
+    rx_gen_ldst(RX_LONG, RX_MEMORY_LD, cpu_pc, cpu_regs[0]);
+    tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], 4);
+    ctx->base.is_jmp = DISAS_JUMP;
+    return true;
+}
+
+/* nop */
+static bool trans_NOP(DisasContext *ctx, arg_NOP *a)
+{
+    return true;
+}
+
+/* scmpu */
+static bool trans_SCMPU(DisasContext *ctx, arg_SCMPU *a)
+{
+    gen_helper_scmpu(cpu_env);
+    return true;
+}
+
+/* smovu */
+static bool trans_SMOVU(DisasContext *ctx, arg_SMOVU *a)
+{
+    gen_helper_smovu(cpu_env);
+    return true;
+}
+
+/* smovf */
+static bool trans_SMOVF(DisasContext *ctx, arg_SMOVF *a)
+{
+    gen_helper_smovf(cpu_env);
+    return true;
+}
+
+/* smovb */
+static bool trans_SMOVB(DisasContext *ctx, arg_SMOVB *a)
+{
+    gen_helper_smovb(cpu_env);
+    return true;
+}
+
+#define STRING(op)                              \
+    do {                                        \
+        TCGv size = tcg_const_i32(a->sz);       \
+        gen_helper_##op(cpu_env, size);         \
+        tcg_temp_free(size);                    \
+    } while (0)
+
+/* suntile */
+static bool trans_SUNTIL(DisasContext *ctx, arg_SUNTIL *a)
+{
+    STRING(suntil);
+    return true;
+}
+
+/* swhile */
+static bool trans_SWHILE(DisasContext *ctx, arg_SWHILE *a)
+{
+    STRING(swhile);
+    return true;
+}
+/* sstr */
+static bool trans_SSTR(DisasContext *ctx, arg_SSTR *a)
+{
+    STRING(sstr);
+    return true;
+}
+
+/* rmpa */
+static bool trans_RMPA(DisasContext *ctx, arg_RMPA *a)
+{
+    STRING(rmpa);
+    return true;
+}
+
+#define MULMAC(op)                                            \
+    do {                                                      \
+        TCGv regs = tcg_const_i32(a->rs << 4 | a->rs2);       \
+        gen_helper_##op(cpu_env, regs);                       \
+        tcg_temp_free(regs);                                  \
+    } while (0)
+
+/* mulhi rs,rs2 */
+static bool trans_MULHI(DisasContext *ctx, arg_MULHI *a)
+{
+    MULMAC(mulhi);
+    return true;
+}
+
+/* mullo rs,rs2 */
+static bool trans_MULLO(DisasContext *ctx, arg_MULLO *a)
+{
+    MULMAC(mullo);
+    return true;
+}
+
+/* machi rs,rs2 */
+static bool trans_MACHI(DisasContext *ctx, arg_MACHI *a)
+{
+    MULMAC(machi);
+    return true;
+}
+
+/* maclo rs,rs2 */
+static bool trans_MACLO(DisasContext *ctx, arg_MACLO *a)
+{
+    MULMAC(maclo);
+    return true;
+}
+
+/* mvfachi rd */
+static bool trans_MVFACHI(DisasContext *ctx, arg_MVFACHI *a)
+{
+    tcg_gen_extrh_i64_i32(cpu_regs[a->rd], cpu_acc);
+    return true;
+}
+
+/* mvfacmi rd */
+static bool trans_MVFACMI(DisasContext *ctx, arg_MVFACMI *a)
+{
+    TCGv_i64 tmp;
+    tmp = tcg_temp_new_i64();
+    tcg_gen_shri_i64(tmp, cpu_acc, 16);
+    tcg_gen_extrl_i64_i32(cpu_regs[a->rd], tmp);
+    tcg_temp_free_i64(tmp);
+    return true;
+}
+
+/* mvtachi rs */
+static bool trans_MVTACHI(DisasContext *ctx, arg_MVTACHI *a)
+{
+    TCGv_i32 hi, lo;
+    hi = tcg_temp_new_i32();
+    lo = tcg_temp_new_i32();
+    tcg_gen_extr_i64_i32(lo, hi, cpu_acc);
+    tcg_gen_concat_i32_i64(cpu_acc, lo, cpu_regs[a->rs]);
+    tcg_temp_free_i32(hi);
+    tcg_temp_free_i32(lo);
+    return true;
+}
+
+/* mvtaclo rs */
+static bool trans_MVTACLO(DisasContext *ctx, arg_MVTACLO *a)
+{
+    TCGv_i32 hi, lo;
+    hi = tcg_temp_new_i32();
+    lo = tcg_temp_new_i32();
+    tcg_gen_extr_i64_i32(lo, hi, cpu_acc);
+    tcg_gen_concat_i32_i64(cpu_acc, cpu_regs[a->rs], hi);
+    tcg_temp_free_i32(hi);
+    tcg_temp_free_i32(lo);
+    return true;
+}
+
+/* racw #imm */
+static bool trans_RACW(DisasContext *ctx, arg_RACW *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->imm + 1);
+    gen_helper_racw(cpu_env, ctx->src);
+    return true;
+}
+
+/* sat rd */
+static bool trans_SAT(DisasContext *ctx, arg_SAT *a)
+{
+    tcg_gen_movi_i32(ctx->src, a->rd);
+    gen_helper_sat(cpu_env, ctx->src);
+    return true;
+}
+
+/* satr */
+static bool trans_SATR(DisasContext *ctx, arg_SATR *a)
+{
+    gen_helper_satr(cpu_env);
+    return true;
+}
+
+#define cat3(a, b, c) a##b##c
+#define FOP(name, op)                                                   \
+    static bool cat3(trans_, name, _ri)(DisasContext *ctx,              \
+                                        cat3(arg_, name, _ri) * a)      \
+    {                                                                   \
+        tcg_gen_movi_i32(ctx->src, li(ctx, 0));                         \
+        gen_helper_##op(cpu_regs[a->rd], cpu_env, cpu_regs[a->rd], ctx->src); \
+        return true;                                                    \
+    }                                                                   \
+    static bool cat3(trans_, name, _rl)(DisasContext *ctx,              \
+                                        cat3(arg_, name, _rl) * a)      \
+    {                                                                   \
+        TCGv val;                                                       \
+        val = rx_load_source(ctx, a->ld, RX_MI_LONG, a->rs);            \
+        gen_helper_##op(cpu_regs[a->rd], cpu_env, cpu_regs[a->rd], val); \
+        return true;                                                    \
+    }
+
+#define FCONVOP(name, op)                                       \
+    static bool trans_##name(DisasContext *ctx, arg_##name * a) \
+    {                                                           \
+        TCGv val;                                               \
+        val = rx_load_source(ctx, a->ld, RX_MI_LONG, a->rs);    \
+        gen_helper_##op(cpu_regs[a->rd], cpu_env, val);         \
+        return true;                                            \
+    }
+
+FOP(FADD, fadd)
+FOP(FSUB, fsub)
+FOP(FMUL, fmul)
+FOP(FDIV, fdiv)
+
+/* fcmp #imm, rd */
+static bool trans_FCMP_ri(DisasContext *ctx, arg_FCMP_ri *a)
+{
+    tcg_gen_movi_i32(ctx->src, li(ctx, 0));
+    gen_helper_fcmp(cpu_env, cpu_regs[a->rd], ctx->src);
+    return true;
+}
+
+/* fcmp dsp[rs], rd */
+/* fcmp rs, rd */
+static bool trans_FCMP_rl(DisasContext *ctx, arg_FCMP_rl *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, RX_MI_LONG, a->rs);
+    gen_helper_fcmp(cpu_env, cpu_regs[a->rd], val);
+    return true;
+}
+
+FCONVOP(FTOI, ftoi)
+FCONVOP(ROUND, round)
+
+/* itof rs, rd */
+/* itof dsp[rs], rd */
+static bool trans_ITOF(DisasContext *ctx, arg_ITOF *a)
+{
+    TCGv val;
+    val = rx_load_source(ctx, a->ld, a->mi, a->rs);
+    gen_helper_itof(cpu_regs[a->rd], cpu_env, val);
+    return true;
+}
+
+static void rx_bsetmem(TCGv mem, TCGv mask)
+{
+    TCGv val;
+    val = tcg_temp_new();
+    rx_gen_ldst(RX_MEMORY_BYTE, RX_MEMORY_LD, val, mem);
+    tcg_gen_or_i32(val, val, mask);
+    rx_gen_ldst(RX_MEMORY_BYTE, RX_MEMORY_ST, val, mem);
+    tcg_temp_free(val);
+}
+
+static void rx_bclrmem(TCGv mem, TCGv mask)
+{
+    TCGv val;
+    val = tcg_temp_new();
+    rx_gen_ldst(RX_MEMORY_BYTE, RX_MEMORY_LD, val, mem);
+    tcg_gen_not_i32(mask, mask);
+    tcg_gen_and_i32(val, val, mask);
+    rx_gen_ldst(RX_MEMORY_BYTE, RX_MEMORY_ST, val, mem);
+    tcg_temp_free(val);
+}
+
+static void rx_btstmem(TCGv mem, TCGv mask)
+{
+    TCGv val;
+    val = tcg_temp_new();
+    rx_gen_ldst(RX_MEMORY_BYTE, RX_MEMORY_LD, val, mem);
+    tcg_gen_and_i32(val, val, mask);
+    tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, val, 0);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, val, 0);
+    tcg_temp_free(val);
+}
+
+static void rx_bnotmem(TCGv mem, TCGv mask)
+{
+    TCGv val;
+    val = tcg_temp_new();
+    rx_gen_ldst(RX_MEMORY_BYTE, RX_MEMORY_LD, val, mem);
+    tcg_gen_xor_i32(val, val, mask);
+    rx_gen_ldst(RX_MEMORY_BYTE, RX_MEMORY_ST, val, mem);
+    tcg_temp_free(val);
+}
+
+static void rx_bsetreg(TCGv reg, TCGv mask)
+{
+    tcg_gen_or_i32(reg, reg, mask);
+}
+
+static void rx_bclrreg(TCGv reg, TCGv mask)
+{
+    tcg_gen_not_i32(mask, mask);
+    tcg_gen_and_i32(reg, reg, mask);
+}
+
+static void rx_btstreg(TCGv reg, TCGv mask)
+{
+    TCGv t0;
+    t0 = tcg_temp_new();
+    tcg_gen_and_i32(t0, reg, mask);
+    tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, t0, 0);
+    tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, t0, 0);
+    tcg_temp_free(t0);
+}
+
+static void rx_bnotreg(TCGv reg, TCGv mask)
+{
+    tcg_gen_xor_i32(reg, reg, mask);
+}
+
+#define BITOP(name, op)                                                 \
+    static bool cat3(trans_, name, _li)(DisasContext *ctx,              \
+                                        cat3(arg_, name, _li) * a)      \
+    {                                                                   \
+        TCGv mask;                                                      \
+        mask = tcg_const_i32(1 << a->imm);                              \
+        rx_index_addr(a->ld, RX_MEMORY_BYTE, a->rs, ctx);               \
+        cat3(rx_, op, mem)(ctx->src, mask);                              \
+        tcg_temp_free(mask);                                            \
+        return true;                                                    \
+    }                                                                   \
+    static bool cat3(trans_, name, _lr)(DisasContext *ctx,              \
+                                        cat3(arg_, name, _lr) * a)      \
+    {                                                                   \
+        TCGv mask;                                                      \
+        mask = tcg_const_i32(1);                                        \
+        tcg_gen_shl_i32(mask, mask, cpu_regs[a->rs2]);                  \
+        switch (a->ld) {                                                \
+        case 0 ... 2:                                                   \
+            rx_index_addr(a->ld, RX_MEMORY_BYTE, a->rs, ctx);           \
+            cat3(rx_, op, mem)(ctx->src, mask);                          \
+            break;                                                      \
+        case 3:                                                         \
+            cat3(rx_, op, reg)(cpu_regs[a->rs], mask);             \
+            break;                                                      \
+        }                                                               \
+        tcg_temp_free(mask);                                            \
+        return true;                                                    \
+    }                                                                   \
+    static bool cat3(trans_, name, _ri)(DisasContext *ctx,              \
+                                        cat3(arg_, name, _ri) * a)      \
+    {                                                                   \
+        TCGv mask;                                                      \
+        mask = tcg_const_i32(1 << a->imm);                              \
+        cat3(rx_, op, reg)(cpu_regs[a->rd], mask);                       \
+        tcg_temp_free(mask);                                            \
+        return true;                                                    \
+    }
+
+BITOP(BSET, bset)
+BITOP(BCLR, bclr)
+BITOP(BTST, btst)
+
+/* bnot rs, dsp[rd] */
+/* bnot rs, rd */
+static bool trans_BNOT_lr(DisasContext *ctx, arg_BNOT_lr *a)
+{
+    TCGv mask;                                                          \
+    mask = tcg_const_i32(1);                                            \
+    tcg_gen_shl_i32(mask, mask, cpu_regs[a->rs2]);
+    switch (a->ld) {
+    case 0 ... 2:
+        rx_index_addr(a->ld, RX_MEMORY_BYTE, a->rs, ctx);
+        rx_bnotmem(ctx->src, mask);
+        break;
+    case 3:
+        rx_bnotreg(cpu_regs[a->rs], mask);
+        break;
+    }
+    tcg_temp_free(mask);
+    return true;
+}
+
+/* bmcond #imm, dsp[rd] */
+/* bnot #imm, dsp[rd] */
+static bool trans_BMCnd_BNOT_mi(DisasContext *ctx, arg_BMCnd_BNOT_mi *a)
+{
+    TCGv bit, cond, val;
+    val = tcg_temp_new();
+    rx_index_addr(a->ld, RX_MEMORY_BYTE, a->rd, ctx);
+    rx_gen_ldst(RX_MEMORY_BYTE, RX_MEMORY_LD, val, ctx->src);
+    if (a->cd == 15) {
+        /* special case bnot #imm, mem */
+        tcg_gen_xori_i32(val, val, 1 << a->imm);
+    } else {
+        cond = tcg_const_i32(a->cd);
+        bit = tcg_const_i32(a->imm);
+        gen_helper_bmcond(val, cpu_env, val, bit, cond);
+        tcg_temp_free(bit);
+        tcg_temp_free(cond);
+    }
+    rx_gen_ldst(RX_MEMORY_BYTE, RX_MEMORY_ST, val, ctx->src);
+    tcg_temp_free(val);
+    return true;
+}
+
+/* bmcond #imm, rd */
+/* bnot #imm, rd */
+static bool trans_BMCnd_BNOT_ri(DisasContext *ctx, arg_BMCnd_BNOT_ri *a)
+{
+    TCGv bit, cond;
+    if (a->cd == 15) {
+        /* special case bnot #imm, reg */
+        tcg_gen_xori_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->imm);
+    } else {
+        cond = tcg_const_i32(a->cd);
+        bit = tcg_const_i32(a->imm);
+        gen_helper_bmcond(cpu_regs[a->rd], cpu_env,
+                          cpu_regs[a->rd], bit, cond);
+        tcg_temp_free(bit);
+        tcg_temp_free(cond);
+    }
+    return true;
+}
+
+static void check_previleged(void)
+{
+    TCGLabel *good;
+
+    good = gen_new_label();
+    tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_psw_pm, 0, good);
+    gen_helper_raise_privilege_violation(cpu_env);
+    gen_set_label(good);
+}
+
+static inline void clrsetpsw(int dst, int mode)
+{
+    TCGv psw[] = {
+        cpu_psw_c, cpu_psw_z, cpu_psw_s, cpu_psw_o,
+        NULL, NULL, NULL, NULL,
+        cpu_psw_i, cpu_psw_u, NULL, NULL,
+        NULL, NULL, NULL, NULL
+    };
+    TCGLabel *skip;
+
+    skip = gen_new_label();
+    if (dst >= 8) {
+        tcg_gen_brcondi_i32(TCG_COND_NE, cpu_psw_pm, 0, skip);
+    }
+    tcg_gen_movi_i32(psw[dst], mode);
+    if (dst == 3) {
+        tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_NONE);
+    }
+    gen_set_label(skip);
+}
+
+/* clrpsw psw */
+static bool trans_CLRPSW(DisasContext *ctx, arg_CLRPSW *a)
+{
+    clrsetpsw(a->cb, 0);
+    return true;
+}
+
+/* setpsw psw */
+static bool trans_SETPSW(DisasContext *ctx, arg_SETPSW *a)
+{
+    clrsetpsw(a->cb, 1);
+    return true;
+}
+
+/* mvtipl #imm */
+static bool trans_MVTIPL(DisasContext *ctx, arg_MVTIPL *a)
+{
+    check_previleged();
+    tcg_gen_movi_i32(cpu_psw_ipl, a->imm);
+    return true;
+}
+
+/* mvtc #imm, rd */
+static bool trans_MVTC_i(DisasContext *ctx, arg_MVTC_i *a)
+{
+    TCGv cr, imm;
+
+    imm = tcg_const_i32(a->imm);
+    cr = tcg_const_i32(a->cr);
+    gen_helper_mvtc(cpu_env, cr, imm);
+    tcg_temp_free(cr);
+    tcg_temp_free(imm);
+    return true;
+}
+
+/* mvtc rs, rd */
+static bool trans_MVTC_r(DisasContext *ctx, arg_MVTC_r *a)
+{
+    TCGv cr;
+
+    cr = tcg_const_i32(a->cr);
+    gen_helper_mvtc(cpu_env, cr, cpu_regs[a->rs]);
+    tcg_temp_free(cr);
+    return true;
+}
+
+/* mvfc rs, rd */
+static bool trans_MVFC(DisasContext *ctx, arg_MVFC *a)
+{
+    TCGv cr;
+
+    cr = tcg_const_i32(a->cr);
+    if (a->cr == 1) {
+        tcg_gen_movi_i32(cpu_regs[a->rd], ctx->base.pc_next - 3);
+    } else {
+        gen_helper_mvfc(cpu_regs[a->rd], cpu_env, cr);
+    }
+    tcg_temp_free(cr);
+    return true;
+}
+
+/* rtfi */
+static bool trans_RTFI(DisasContext *ctx, arg_RTFI *a)
+{
+    check_previleged();
+    tcg_gen_mov_i32(cpu_pc, cpu_bpc);
+    tcg_gen_mov_i32(cpu_psw, cpu_bpsw);
+    gen_helper_unpack_psw(cpu_env);
+    ctx->base.is_jmp = DISAS_JUMP;
+    return true;
+}
+
+/* rtfe */
+static bool trans_RTE(DisasContext *ctx, arg_RTE *a)
+{
+    check_previleged();
+    rx_gen_ldst(RX_LONG, RX_MEMORY_LD, cpu_pc, cpu_regs[0]);
+    tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], 4);
+    rx_gen_ldst(RX_LONG, RX_MEMORY_LD, cpu_psw, cpu_regs[0]);
+    tcg_gen_addi_i32(cpu_regs[0], cpu_regs[0], 4);
+    gen_helper_unpack_psw(cpu_env);
+    ctx->base.is_jmp = DISAS_JUMP;
+    return true;
+}
+
+/* brk */
+static bool trans_BRK(DisasContext *ctx, arg_BRK *a)
+{
+    tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next);
+    gen_helper_rxbrk(cpu_env);
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return true;
+}
+
+/* int #imm */
+static bool trans_INT(DisasContext *ctx, arg_INT *a)
+{
+    TCGv vec;
+
+    vec = tcg_const_i32(a->imm & 0xff);
+    tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next);
+    gen_helper_rxint(cpu_env, vec);
+    tcg_temp_free(vec);
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return true;
+}
+
+/* wait */
+static bool trans_WAIT(DisasContext *ctx, arg_WAIT *a)
+{
+    check_previleged();
+    tcg_gen_addi_i32(cpu_pc, cpu_pc, 2);
+    gen_helper_wait(cpu_env);
+    return true;
+}
+
+static void rx_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
+{
+    DisasContext *ctx = container_of(dcbase, DisasContext, base);
+
+    ctx->src = tcg_temp_new();
+}
+
+static void rx_tr_tb_start(DisasContextBase *dcbase, CPUState *cs)
+{
+}
+
+static void rx_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
+{
+    DisasContext *ctx = container_of(dcbase, DisasContext, base);
+
+    tcg_gen_insn_start(ctx->base.pc_next);
+}
+
+static bool rx_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
+                                    const CPUBreakpoint *bp)
+{
+    DisasContext *ctx = container_of(dcbase, DisasContext, base);
+
+    /* We have hit a breakpoint - make sure PC is up-to-date */
+    gen_save_cpu_state(ctx, true);
+    gen_helper_debug(cpu_env);
+    ctx->base.is_jmp = DISAS_NORETURN;
+    ctx->base.pc_next += 1;
+    return true;
+}
+
+static void rx_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
+{
+    CPURXState *env = cs->env_ptr;
+    DisasContext *ctx = container_of(dcbase, DisasContext, base);
+    uint32_t insn;
+
+    ctx->env = env;
+    insn = decode_load(ctx);
+    if (!decode(ctx, insn)) {
+        gen_helper_raise_illegal_instruction(cpu_env);
+    }
+}
+
+static void rx_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
+{
+    DisasContext *ctx = container_of(dcbase, DisasContext, base);
+
+    switch (ctx->base.is_jmp) {
+    case DISAS_NEXT:
+    case DISAS_TOO_MANY:
+        gen_save_cpu_state(ctx, false);
+        gen_goto_tb(ctx, 0, dcbase->pc_next);
+        break;
+    case DISAS_JUMP:
+        if (ctx->base.singlestep_enabled) {
+            gen_helper_debug(cpu_env);
+        } else {
+            tcg_gen_lookup_and_goto_ptr();
+        }
+        break;
+    case DISAS_NORETURN:
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void rx_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs)
+{
+    qemu_log("IN:\n");  /* , lookup_symbol(dcbase->pc_first)); */
+    log_target_disas(cs, dcbase->pc_first, dcbase->tb->size);
+}
+
+static const TranslatorOps rx_tr_ops = {
+    .init_disas_context = rx_tr_init_disas_context,
+    .tb_start           = rx_tr_tb_start,
+    .insn_start         = rx_tr_insn_start,
+    .breakpoint_check   = rx_tr_breakpoint_check,
+    .translate_insn     = rx_tr_translate_insn,
+    .tb_stop            = rx_tr_tb_stop,
+    .disas_log          = rx_tr_disas_log,
+};
+
+void gen_intermediate_code(CPUState *cs, TranslationBlock *tb)
+{
+    DisasContext dc;
+
+    translator_loop(&rx_tr_ops, &dc.base, cs, tb);
+}
+
+void restore_state_to_opc(CPURXState *env, TranslationBlock *tb,
+                          target_ulong *data)
+{
+    env->pc = data[0];
+    env->psw = data[1];
+    rx_cpu_unpack_psw(env, 1);
+}
+
+#define ALLOC_REGISTER(sym, name) \
+    cpu_##sym = tcg_global_mem_new_i32(cpu_env, \
+                                       offsetof(CPURXState, sym), name)
+
+void rx_translate_init(void)
+{
+    static const char * const regnames[16] = {
+        "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
+        "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15"
+    };
+    int i;
+
+    for (i = 0; i < 16; i++) {
+        cpu_regs[i] = tcg_global_mem_new_i32(cpu_env,
+                                              offsetof(CPURXState, regs[i]),
+                                              regnames[i]);
+    }
+    ALLOC_REGISTER(pc, "PC");
+    ALLOC_REGISTER(psw, "PSW");
+    ALLOC_REGISTER(psw_o, "PSW(O)");
+    ALLOC_REGISTER(psw_s, "PSW(S)");
+    ALLOC_REGISTER(psw_z, "PSW(Z)");
+    ALLOC_REGISTER(psw_c, "PSW(C)");
+    ALLOC_REGISTER(psw_u, "PSW(U)");
+    ALLOC_REGISTER(psw_i, "PSW(I)");
+    ALLOC_REGISTER(psw_pm, "PSW(PM)");
+    ALLOC_REGISTER(psw_ipl, "PSW(IPL)");
+    ALLOC_REGISTER(usp, "USP");
+    ALLOC_REGISTER(fpsw, "FPSW");
+    ALLOC_REGISTER(bpsw, "BPSW");
+    ALLOC_REGISTER(bpc, "BPC");
+    ALLOC_REGISTER(isp, "ISP");
+    ALLOC_REGISTER(fintv, "FINTV");
+    ALLOC_REGISTER(intb, "INTB");
+    cpu_acc = tcg_global_mem_new_i64(cpu_env,
+                                     offsetof(CPURXState, acc), "ACC");
+    cpu_pswop = tcg_global_mem_new_i32(cpu_env,
+                                     offsetof(CPURXState, psw_op), "");
+    for (i = 0; i < 3; i++) {
+        cpu_pswop_v[i] = tcg_global_mem_new_i32(cpu_env,
+                                                offsetof(CPURXState, psw_v[i]),
+                                                "");
+    }
+}
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 02/11] target/rx: TCG helper
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 01/11] target/rx: TCG Translation Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 03/11] target/rx: CPU definition Yoshinori Sato
                     ` (11 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 target/rx/helper.c    | 252 +++++++++++++++++++++
 target/rx/helper.h    |  39 ++++
 target/rx/op_helper.c | 602 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 893 insertions(+)
 create mode 100644 target/rx/helper.c
 create mode 100644 target/rx/helper.h
 create mode 100644 target/rx/op_helper.c

diff --git a/target/rx/helper.c b/target/rx/helper.c
new file mode 100644
index 0000000000..9b0d0eacb4
--- /dev/null
+++ b/target/rx/helper.c
@@ -0,0 +1,252 @@
+/*
+ *  RX emulation
+ *
+ *  Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "cpu.h"
+#include "exec/log.h"
+#include "exec/cpu_ldst.h"
+#include "sysemu/sysemu.h"
+
+uint32_t update_psw_o(CPURXState *env)
+{
+    int o;
+
+    switch (env->psw_op) {
+    case RX_PSW_OP_NONE:
+        return env->psw_o;
+    case RX_PSW_OP_ADD: {
+        uint32_t r1, r2;
+        r1 = ~(env->psw_v[0] ^ env->psw_v[1]);
+        r2 = (env->psw_v[0] ^ env->psw_v[2]);
+        o = (r1 & r2) >> 31;
+        break;
+    }
+    case RX_PSW_OP_SUB: {
+        uint32_t r1, r2;
+        r1 = (env->psw_v[0] ^ env->psw_v[1]);
+        r2 = (env->psw_v[0] ^ env->psw_v[2]);
+        o = (r1 & r2) >> 31;
+        break;
+    }
+    case RX_PSW_OP_SHLL: {
+        uint32_t m, v;
+        m = (1 << env->psw_v[1]) - 1;
+        v = env->psw_v[0] >> (32 - env->psw_v[1]);
+        o = (v == 0) || (v == m);
+        break;
+    }
+    default:
+        g_assert_not_reached();
+        return -1;
+    }
+    env->psw_o = o;
+    env->psw_op = RX_PSW_OP_NONE;
+    return o;
+}
+
+uint32_t rx_get_psw_low(CPURXState *env)
+{
+    return (update_psw_o(env) << 3) |
+        (env->psw_s << 2) |
+        (env->psw_z << 1) |
+        (env->psw_c << 0);
+}
+
+uint32_t psw_cond(CPURXState *env, uint32_t cond)
+{
+    uint32_t c, z, s, o;
+
+    switch (cond) {
+    case 0: /* z */
+        return env->psw_z != 0;
+    case 1: /* nz */
+        return env->psw_z == 0;
+    case 2: /* c */
+        return env->psw_c != 0;
+    case 3: /* nc */
+        return env->psw_c == 0;
+    case 4: /* gtu (C&^Z) == 1 */
+    case 5: /* leu (C&^Z) == 0 */
+        c = env->psw_c != 0;
+        z = env->psw_z != 0;
+        return (c && !z) == (5 - cond);
+    case 6: /* pz (S == 0) */
+        return env->psw_s == 0;
+    case 7: /* n (S == 1) */
+        return env->psw_s != 0;
+    case 8: /* ge (S^O)==0 */
+    case 9: /* lt (S^O)==1 */
+        s = env->psw_s != 0;
+        o = update_psw_o(env);
+        return (s | o) == (cond - 8);
+    case 10: /* gt ((S^O)|Z)==0 */
+    case 11: /* le ((S^O)|Z)==1 */
+        s = env->psw_s != 0;
+        o = update_psw_o(env);
+        z = env->psw_z != 0;
+        return ((s ^ o) | z) == (cond - 10);
+    case 12: /* o */
+        return update_psw_o(env) != 0;
+    case 13: /* no */
+        return update_psw_o(env) == 0;
+    case 14: /* always true */
+        return 1;
+    case 15:
+        return 0;
+    default:
+        g_assert_not_reached();
+        return -1;
+    }
+}
+
+void rx_cpu_unpack_psw(CPURXState *env, int all)
+{
+    if (env->psw_pm == 0) {
+        env->psw_ipl = (env->psw >> 24) & 15;
+        if (all) {
+            env->psw_pm = (env->psw >> 20) & 1;
+        }
+        env->psw_u =  (env->psw >> 17) & 1;
+        env->psw_i =  (env->psw >> 16) & 1;
+    }
+    env->psw_o =  (env->psw >> 3) & 1;
+    env->psw_s =  (env->psw >> 2) & 1;
+    env->psw_z =  (env->psw >> 1) & 1;
+    env->psw_c =  (env->psw >> 0) & 1;
+    env->psw_op = RX_PSW_OP_NONE;
+}
+
+void rx_cpu_do_interrupt(CPUState *cs)
+{
+    RXCPU *cpu = RXCPU(cs);
+    CPURXState *env = &cpu->env;
+    int do_irq = cs->interrupt_request &
+        (CPU_INTERRUPT_HARD | CPU_INTERRUPT_SOFT | CPU_INTERRUPT_FIR);
+    int irq_vector = -1;
+
+    env->in_sleep = 0;
+
+    if (do_irq & CPU_INTERRUPT_HARD) {
+        irq_vector = env->irq;
+        cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
+    }
+    if (irq_vector == -1 && do_irq & CPU_INTERRUPT_SOFT) {
+        irq_vector = env->sirq;
+        cs->interrupt_request &= ~CPU_INTERRUPT_SOFT;
+    }
+
+    if (qemu_loglevel_mask(CPU_LOG_INT)) {
+        if (cs->exception_index < 0x100) {
+            const char *expname;
+            switch (cs->exception_index) {
+            case 20:
+                expname = "previlage_violation";
+                break;
+            case 21:
+                expname = "access_exception";
+                break;
+            case 23:
+                expname = "illegal_instruction";
+                break;
+            case 25:
+                expname = "fpu_exception";
+                break;
+            case 30:
+                expname = "NMI_interrupt";
+                break;
+            }
+            qemu_log("exception 0x%02x [%s] raised\n",
+                     cs->exception_index, expname);
+        } else {
+            if (do_irq & CPU_INTERRUPT_FIR)
+                qemu_log("fast interrupt raised\n");
+            else
+                qemu_log("interrupt 0x%02x raised\n",
+                         irq_vector);
+        }
+        log_cpu_state(cs, 0);
+    }
+    if (env->psw_u) {
+        env->usp = env->regs[0];
+    } else {
+        env->isp = env->regs[0];
+    }
+    update_psw_o(env);
+    env->psw = pack_psw(env);
+    if ((do_irq & CPU_INTERRUPT_FIR) == 0) {
+        env->isp -= 4;
+        cpu_stl_all(env, env->isp, env->psw);
+        env->isp -= 4;
+        cpu_stl_all(env, env->isp, env->pc);
+    } else {
+        env->bpc = env->pc;
+        env->bpsw = env->psw;
+    }
+    env->psw_pm = env->psw_i = env->psw_u = 0;
+    env->regs[0] = env->isp;
+    if (do_irq) {
+        if (do_irq & CPU_INTERRUPT_FIR) {
+            env->pc = env->fintv;
+            env->psw_ipl = 15;
+            cs->interrupt_request &= ~CPU_INTERRUPT_FIR;
+            qemu_set_irq(env->ack, 0);
+            return;
+        } else if (do_irq & CPU_INTERRUPT_HARD) {
+            env->psw_ipl = env->intlevel;
+            qemu_set_irq(env->ack, 0);
+        }
+        env->pc = cpu_ldl_all(env, env->intb + irq_vector * 4);
+        return;
+    } else {
+        uint32_t vec = cs->exception_index;
+        env->pc = cpu_ldl_all(env, 0xffffffc0 + vec * 4);
+        return;
+    }
+}
+
+bool rx_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+    RXCPU *cpu = RXCPU(cs);
+    CPURXState *env = &cpu->env;
+    int accept = 0;
+    /* software interrupt */
+    if (interrupt_request & CPU_INTERRUPT_SOFT) {
+        accept = 1;
+    }
+    /* hardware interrupt (Normal) */
+    if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+        env->psw_i && (env->psw_ipl < env->intlevel)) {
+        accept = 1;
+    }
+    /* hardware interrupt (FIR) */
+    if ((interrupt_request & CPU_INTERRUPT_FIR) &&
+        env->psw_i && (env->psw_ipl < 15)) {
+        accept = 1;
+    }
+    if (accept) {
+        rx_cpu_do_interrupt(cs);
+        return true;
+    }
+    return false;
+}
+
+hwaddr rx_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+    return addr;
+}
diff --git a/target/rx/helper.h b/target/rx/helper.h
new file mode 100644
index 0000000000..06a3d29486
--- /dev/null
+++ b/target/rx/helper.h
@@ -0,0 +1,39 @@
+DEF_HELPER_1(raise_illegal_instruction, noreturn, env)
+DEF_HELPER_1(raise_access_fault, noreturn, env)
+DEF_HELPER_1(raise_privilege_violation, noreturn, env)
+DEF_HELPER_1(wait, noreturn, env)
+DEF_HELPER_1(debug, noreturn, env)
+DEF_HELPER_2(rxint, noreturn, env, i32)
+DEF_HELPER_1(rxbrk, noreturn, env)
+DEF_HELPER_FLAGS_3(fadd, TCG_CALL_NO_WG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fsub, TCG_CALL_NO_WG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fmul, TCG_CALL_NO_WG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fdiv, TCG_CALL_NO_WG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fcmp, TCG_CALL_NO_WG, void, env, f32, f32)
+DEF_HELPER_FLAGS_2(ftoi, TCG_CALL_NO_WG, i32, env, f32)
+DEF_HELPER_FLAGS_2(round, TCG_CALL_NO_WG, i32, env, f32)
+DEF_HELPER_FLAGS_2(itof, TCG_CALL_NO_WG, f32, env, i32)
+DEF_HELPER_FLAGS_2(racw, TCG_CALL_NO_WG, void, env, i32)
+DEF_HELPER_1(psw_o, i32, env)
+DEF_HELPER_3(mvtc, void, env, i32, i32)
+DEF_HELPER_2(mvfc, i32, env, i32)
+DEF_HELPER_FLAGS_2(sccond, TCG_CALL_NO_WG, i32, env, i32)
+DEF_HELPER_FLAGS_4(brcond, TCG_CALL_NO_WG, i32, env, i32, i32, i32)
+DEF_HELPER_FLAGS_4(bmcond, TCG_CALL_NO_WG, i32, env, i32, i32, i32)
+DEF_HELPER_1(unpack_psw, void, env)
+DEF_HELPER_FLAGS_3(div, TCG_CALL_NO_WG, i32, env, i32, i32)
+DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32)
+DEF_HELPER_FLAGS_1(scmpu, TCG_CALL_NO_WG, void, env)
+DEF_HELPER_1(smovu, void, env)
+DEF_HELPER_1(smovf, void, env)
+DEF_HELPER_1(smovb, void, env)
+DEF_HELPER_2(sstr, void, env, i32)
+DEF_HELPER_FLAGS_2(swhile, TCG_CALL_NO_WG, void, env, i32)
+DEF_HELPER_FLAGS_2(suntil, TCG_CALL_NO_WG, void, env, i32)
+DEF_HELPER_FLAGS_2(rmpa, TCG_CALL_NO_WG, void, env, i32)
+DEF_HELPER_2(mulhi, void, env, i32)
+DEF_HELPER_2(mullo, void, env, i32)
+DEF_HELPER_2(machi, void, env, i32)
+DEF_HELPER_2(maclo, void, env, i32)
+DEF_HELPER_2(sat, void, env, i32)
+DEF_HELPER_1(satr, void, env)
diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c
new file mode 100644
index 0000000000..572e0df45e
--- /dev/null
+++ b/target/rx/op_helper.c
@@ -0,0 +1,602 @@
+/*
+ *  RX helper functions
+ *
+ *  Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
+#include "exec/cpu_ldst.h"
+#include "fpu/softfloat.h"
+
+#define OP_SMOVU 1
+#define OP_SMOVF 0
+#define OP_SMOVB 2
+
+#define OP_SWHILE 0
+#define OP_SUNTIL 4
+
+static void set_fpmode(CPURXState *env, uint32_t val);
+static inline void QEMU_NORETURN raise_exception(CPURXState *env, int index,
+                                                 uintptr_t retaddr);
+
+/* psw operations */
+uint32_t helper_psw_o(CPURXState *env)
+{
+    return update_psw_o(env);
+}
+
+uint32_t helper_brcond(CPURXState *env, uint32_t cond, uint32_t t, uint32_t f)
+{
+    return psw_cond(env, cond) ? t : f;
+}
+
+uint32_t helper_sccond(CPURXState *env, uint32_t cond)
+{
+    return psw_cond(env, cond);
+}
+
+uint32_t helper_bmcond(CPURXState *env, uint32_t src,
+                       uint32_t bit, uint32_t cond)
+{
+    uint32_t mask = 1 << bit;
+    if (psw_cond(env, cond)) {
+        return src |= mask;
+    } else {
+        return src &= ~mask;
+    }
+}
+
+uint32_t helper_mvfc(CPURXState *env, uint32_t cr)
+{
+    switch (cr) {
+    case 0:
+        update_psw_o(env);
+        return pack_psw(env);
+    case 2:
+        return env->psw_u ? env->regs[0] : env->usp;
+    case 3:
+        return env->fpsw;
+    case 8:
+        return env->bpsw;
+    case 9:
+        return env->bpc;
+    case 10:
+        return env->psw_u ? env->isp : env->regs[0];
+    case 11:
+        return env->fintv;
+    case 12:
+        return env->intb;
+    default:
+        g_assert_not_reached();
+        return -1;
+    }
+}
+
+void helper_mvtc(CPURXState *env, uint32_t cr, uint32_t val)
+{
+    switch (cr) {
+    case 0:
+        env->psw = val;
+        rx_cpu_unpack_psw(env, 0);
+        break;
+    case 2:
+        env->usp = val;;
+        if (env->psw_u) {
+            env->regs[0] = val;
+        }
+        break;
+    case 3:
+        env->fpsw = val;
+        set_fpmode(env, val);
+        break;
+    case 8:
+        env->bpsw = val;
+        break;
+    case 9:
+        env->bpc = val;
+        break;
+    case 10:
+        env->isp = val;
+        if (!env->psw_u) {
+            env->regs[0] = val;
+        }
+        break;
+    case 11:
+        env->fintv = val;
+        break;
+    case 12:
+        env->intb = val;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+void helper_unpack_psw(CPURXState *env)
+{
+    uint32_t prev_u;
+    prev_u = env->psw_u;
+    rx_cpu_unpack_psw(env, 1);
+    if (prev_u != env->psw_u) {
+        if (env->psw_u) {
+            env->isp = env->regs[0];
+            env->regs[0] = env->usp;
+        } else {
+            env->usp = env->regs[0];
+            env->regs[0] = env->isp;
+        }
+    }
+}
+
+/* fp operations */
+static void update_fpsw(CPURXState *env, float32 ret, uintptr_t retaddr)
+{
+    int xcpt, cause, enable;
+
+    env->psw_z = (*((uint32_t *)&ret) == 0); \
+    env->psw_s = (*((uint32_t *)&ret) >= 0x80000000UL); \
+
+    xcpt = get_float_exception_flags(&env->fp_status);
+
+    /* Clear the cause entries */
+    env->fpsw &= ~FPSW_CAUSE_MASK;
+
+    if (unlikely(xcpt)) {
+        if (xcpt & float_flag_invalid) {
+            env->fpsw |= FPSW_CAUSE_V;
+        }
+        if (xcpt & float_flag_divbyzero) {
+            env->fpsw |= FPSW_CAUSE_Z;
+        }
+        if (xcpt & float_flag_overflow) {
+            env->fpsw |= FPSW_CAUSE_O;
+        }
+        if (xcpt & float_flag_underflow) {
+            env->fpsw |= FPSW_CAUSE_U;
+        }
+        if (xcpt & float_flag_inexact) {
+            env->fpsw |= FPSW_CAUSE_X;
+        }
+
+        /* Accumulate in flag entries */
+        env->fpsw |= (env->fpsw & FPSW_CAUSE_MASK)
+                      << (FPSW_FLAG_SHIFT - FPSW_CAUSE_SHIFT);
+        env->fpsw |= ((env->fpsw >> FPSW_FLAG_V) |
+                      (env->fpsw >> FPSW_FLAG_O) |
+                      (env->fpsw >> FPSW_FLAG_Z) |
+                      (env->fpsw >> FPSW_FLAG_U) |
+                      (env->fpsw >> FPSW_FLAG_X)) << FPSW_FLAG_S;
+
+        /* Generate an exception if enabled */
+        cause = (env->fpsw & FPSW_CAUSE_MASK) >> FPSW_CAUSE_SHIFT;
+        enable = (env->fpsw & FPSW_ENABLE_MASK) >> FPSW_ENABLE_SHIFT;
+        if (cause & enable) {
+            raise_exception(env, 21, retaddr);
+        }
+    }
+}
+
+static void set_fpmode(CPURXState *env, uint32_t val)
+{
+    static const int roundmode[] = {
+        float_round_nearest_even,
+        float_round_to_zero,
+        float_round_up,
+        float_round_down,
+    };
+    env->fpsw = val & FPSW_MASK;
+    set_float_rounding_mode(roundmode[val & FPSW_RM_MASK],
+                            &env->fp_status);
+    set_flush_to_zero((val & FPSW_DN) != 0, &env->fp_status);
+}
+
+#define FLOATOP(op, func)                                           \
+float32 helper_##op(CPURXState *env, float32 t0, float32 t1) \
+{ \
+    float32 ret; \
+    ret = func(t0, t1, &env->fp_status); \
+    update_fpsw(env, *(uint32_t *)&ret, GETPC());        \
+    return ret; \
+}
+
+FLOATOP(fadd, float32_add)
+FLOATOP(fsub, float32_sub)
+FLOATOP(fmul, float32_mul)
+FLOATOP(fdiv, float32_div)
+
+void helper_fcmp(CPURXState *env, float32 t0, float32 t1)
+{
+    int st;
+    st = float32_compare(t0, t1, &env->fp_status);
+    update_fpsw(env, 0, GETPC());
+    env->psw_z = env->psw_s = env->psw_o = 0;
+    env->psw_op = RX_PSW_OP_NONE;
+    switch (st) {
+    case float_relation_equal:
+        env->psw_z = 1;
+        break;
+    case float_relation_less:
+        env->psw_s = 1;
+        break;
+    case float_relation_unordered:
+        env->psw_o = 1;
+        break;
+    }
+}
+
+uint32_t helper_ftoi(CPURXState *env, float32 t0)
+{
+    uint32_t ret;
+    ret = float32_to_int32_round_to_zero(t0, &env->fp_status);
+    update_fpsw(env, ret, GETPC());
+    return ret;
+}
+
+uint32_t helper_round(CPURXState *env, float32 t0)
+{
+    uint32_t ret;
+    ret = float32_to_int32(t0, &env->fp_status);
+    update_fpsw(env, ret, GETPC());
+    return ret;
+}
+
+float32 helper_itof(CPURXState *env, uint32_t t0)
+{
+    float32 ret;
+    ret = int32_to_float32(t0, &env->fp_status);
+    update_fpsw(env, *(uint32_t *)&ret, GETPC());
+    return ret;
+}
+
+/* string operations */
+void helper_scmpu(CPURXState *env)
+{
+    uint8_t tmp0, tmp1;
+    if (env->regs[3] == 0) {
+        return;
+    }
+    while (env->regs[3] != 0) {
+        tmp0 = cpu_ldub_data_ra(env, env->regs[1]++, GETPC());
+        tmp1 = cpu_ldub_data_ra(env, env->regs[2]++, GETPC());
+        env->regs[3]--;
+        if (tmp0 != tmp1 || tmp0 == '\0') {
+            break;
+        }
+    }
+    env->psw_z = (tmp0 == tmp1);
+    env->psw_c = (tmp0 >= tmp1);
+}
+
+void helper_sstr(CPURXState *env, uint32_t sz)
+{
+    while (env->regs[3] != 0) {
+        switch (sz) {
+        case 0:
+            cpu_stb_data_ra(env, env->regs[1], env->regs[2], GETPC());
+            break;
+        case 1:
+            cpu_stw_data_ra(env, env->regs[1], env->regs[2], GETPC());
+            break;
+        case 2:
+            cpu_stl_data_ra(env, env->regs[1], env->regs[2], GETPC());
+            break;
+        }
+        env->regs[1] += (1 << sz);
+        env->regs[3]--;
+    }
+}
+
+static void smov(uint32_t mode, CPURXState *env)
+{
+    uint8_t tmp;
+    int dir;
+
+    dir = (mode & OP_SMOVB) ? -1 : 1;
+    while (env->regs[3] != 0) {
+        tmp = cpu_ldub_data_ra(env, env->regs[2], env->pc);
+        cpu_stb_data_ra(env, env->regs[1], tmp, env->pc);
+        env->regs[1] += dir;
+        env->regs[2] += dir;
+        env->regs[3]--;
+        if ((mode & OP_SMOVU) && tmp == 0) {
+            break;
+        }
+    }
+}
+
+void helper_smovu(CPURXState *env)
+{
+    smov(OP_SMOVU, env);
+}
+
+void helper_smovf(CPURXState *env)
+{
+    smov(OP_SMOVF, env);
+}
+
+void helper_smovb(CPURXState *env)
+{
+    smov(OP_SMOVB, env);
+}
+
+static uint32_t (* const ld[])(CPUArchState *env,
+                               target_ulong ptr,
+                               uintptr_t retaddr) = {
+    cpu_ldub_data_ra, cpu_lduw_data_ra, cpu_ldl_data_ra,
+};
+
+static void rx_search(uint32_t mode, int sz, CPURXState *env)
+{
+    uint32_t tmp;
+
+    while (env->regs[3] != 0) {
+        tmp = ld[sz](env, env->regs[1], env->pc);
+        env->regs[1] += 1 << (mode % 4);
+        env->regs[3]--;
+        if ((mode == OP_SWHILE && tmp != env->regs[2]) ||
+            (mode == OP_SUNTIL && tmp == env->regs[2])) {
+            break;
+        }
+    }
+    env->psw_z = (mode == OP_SUNTIL) ?
+        (tmp == env->regs[2]) : (env->regs[3] == 0);
+    env->psw_c = (tmp <= env->regs[2]);
+}
+
+void helper_suntil(CPURXState *env, uint32_t sz)
+{
+    rx_search(OP_SUNTIL, sz, env);
+}
+
+void helper_swhile(CPURXState *env, uint32_t sz)
+{
+    rx_search(OP_SWHILE, sz, env);
+}
+
+/* accumlator operations */
+void helper_rmpa(CPURXState *env, uint32_t sz)
+{
+    uint64_t result_l, prev;
+    int32_t result_h;
+    int64_t tmp0, tmp1;
+
+    if (env->regs[3] == 0) {
+        return;
+    }
+    result_l = env->regs[5];
+    result_l <<= 32;
+    result_l |= env->regs[4];
+    result_h = env->regs[6];
+    env->psw_o = 0;
+
+    while (env->regs[3] != 0) {
+        tmp0 = ld[sz](env, env->regs[1], env->pc);
+        tmp1 = ld[sz](env, env->regs[2], env->pc);
+        tmp0 *= tmp1;
+        prev = result_l;
+        result_l += tmp0;
+        /* carry / bollow */
+        if (tmp0 < 0) {
+            if (prev > result_l) {
+                result_h--;
+            }
+        } else {
+            if (prev < result_l) {
+                result_h++;
+            }
+        }
+
+        env->regs[1] += 1 << sz;
+        env->regs[2] += 1 << sz;
+    }
+    env->psw_s = (result_h < 0);
+    env->psw_o = (result_h != 0 && result_h != -1);
+    env->psw_op = RX_PSW_OP_NONE;
+    env->regs[6] = result_h;
+    env->regs[5] = result_l >> 32;
+    env->regs[4] = result_l & 0xffffffff;
+}
+
+void helper_mulhi(CPURXState *env, uint32_t regs)
+{
+    int rs, rs2;
+    long long tmp0, tmp1;
+
+    rs = (regs >> 4) & 15;
+    rs2 = regs & 15;
+
+    tmp0 = env->regs[rs] >> 16;
+    tmp1 = env->regs[rs2] >> 16;
+    env->acc = (tmp0 * tmp1) << 16;
+}
+
+void helper_mullo(CPURXState *env, uint32_t regs)
+{
+    int rs, rs2;
+    long long tmp0, tmp1;
+
+    rs = (regs >> 4) & 15;
+    rs2 = regs & 15;
+
+    tmp0 = env->regs[rs] & 0xffff;
+    tmp1 = env->regs[rs2] & 0xffff;
+    env->acc = (tmp0 * tmp1) << 16;
+}
+
+void helper_machi(CPURXState *env, uint32_t regs)
+{
+    int rs, rs2;
+    long long tmp0, tmp1;
+
+    rs = (regs >> 4) & 15;
+    rs2 = regs & 15;
+
+    tmp0 = env->regs[rs] >> 16;
+    tmp1 = env->regs[rs2] >> 16;
+    env->acc += (tmp0 * tmp1) << 16;
+}
+
+void helper_maclo(CPURXState *env, uint32_t regs)
+{
+    int rs, rs2;
+    long long tmp0, tmp1;
+
+    rs = (regs >> 4) & 15;
+    rs2 = regs & 15;
+
+    tmp0 = env->regs[rs] & 0xffff;
+    tmp1 = env->regs[rs2] & 0xffff;
+    env->acc += (tmp0 * tmp1) << 16;
+}
+
+void helper_racw(CPURXState *env, uint32_t imm)
+{
+    int64_t acc;
+    acc = env->acc;
+    acc <<= (imm + 1);
+    acc += 0x0000000080000000LL;
+    if (acc > 0x00007fff00000000LL) {
+        acc = 0x00007fff00000000LL;
+    } else if (acc < -0x800000000000LL) {
+        acc = -0x800000000000LL;
+    } else {
+        acc &= 0xffffffff00000000LL;
+    }
+    env->acc = acc;
+}
+
+void helper_sat(CPURXState *env, uint32_t reg)
+{
+    uint32_t o;
+    o = update_psw_o(env);
+    if (o == 1 && env->psw_s == 1) {
+        env->regs[reg] = 0x7fffffff;
+    } else if (o == 1 && env->psw_s == 0) {
+        env->regs[reg] = 0x80000000;
+    }
+}
+
+void helper_satr(CPURXState *env)
+{
+    uint32_t o;
+    o = update_psw_o(env);
+    if (o == 1 && env->psw_s == 1) {
+        env->regs[4] = 0x00000000;
+        env->regs[5] = 0x7fffffff;
+        env->regs[6] = 0xffffffff;
+    } else if (o == 1 && env->psw_s == 0) {
+        env->regs[4] = 0xffffffff;
+        env->regs[5] = 0x80000000;
+        env->regs[6] = 0x00000000;
+    }
+}
+
+/* div */
+uint32_t helper_div(CPURXState *env, uint32_t num, uint32_t den)
+{
+    uint32_t ret = num;
+    if (den != 0) {
+        ret = (int32_t)num / (int32_t)den;
+    }
+    env->psw_o = (ret == 0 || den == 0);
+    env->psw_op = RX_PSW_OP_NONE;
+    return ret;
+}
+
+uint32_t helper_divu(CPURXState *env, uint32_t num, uint32_t den)
+{
+    uint32_t ret = num;
+    if (den != 0) {
+        ret = num / den;
+    }
+    env->psw_o = (den == 0);
+    env->psw_op = RX_PSW_OP_NONE;
+    return ret;
+}
+
+/* exception */
+static inline void QEMU_NORETURN raise_exception(CPURXState *env, int index,
+                                                 uintptr_t retaddr)
+{
+    CPUState *cs = CPU(rx_env_get_cpu(env));
+
+    cs->exception_index = index;
+    cpu_loop_exit_restore(cs, retaddr);
+}
+
+void QEMU_NORETURN helper_raise_privilege_violation(CPURXState *env)
+{
+    raise_exception(env, 20, GETPC());
+}
+
+void QEMU_NORETURN helper_raise_access_fault(CPURXState *env)
+{
+    raise_exception(env, 21, GETPC());
+}
+
+void QEMU_NORETURN helper_raise_illegal_instruction(CPURXState *env)
+{
+    raise_exception(env, 23, GETPC());
+}
+
+void QEMU_NORETURN helper_wait(CPURXState *env)
+{
+    CPUState *cs = CPU(rx_env_get_cpu(env));
+
+    cs->halted = 1;
+    env->in_sleep = 1;
+    raise_exception(env, EXCP_HLT, 0);
+}
+
+void QEMU_NORETURN helper_debug(CPURXState *env)
+{
+    CPUState *cs = CPU(rx_env_get_cpu(env));
+
+    cs->exception_index = EXCP_DEBUG;
+    cpu_loop_exit(cs);
+}
+
+void QEMU_NORETURN helper_rxint(CPURXState *env, uint32_t vec)
+{
+    CPUState *cs = CPU(rx_env_get_cpu(env));
+
+    cs->interrupt_request |= CPU_INTERRUPT_SOFT;
+    env->sirq = vec;
+    raise_exception(env, 0x100, 0);
+}
+
+void QEMU_NORETURN helper_rxbrk(CPURXState *env)
+{
+    CPUState *cs = CPU(rx_env_get_cpu(env));
+
+    cs->interrupt_request |= CPU_INTERRUPT_SOFT;
+    env->sirq = 0;
+    raise_exception(env, 0x100, 0);
+}
+
+void tlb_fill(CPUState *cs, target_ulong addr, int size,
+              MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
+{
+    uint32_t address, physical, prot;
+
+    /* Linear mapping */
+    address = physical = addr & TARGET_PAGE_MASK;
+    prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+    tlb_set_page(cs, address, physical, prot, mmu_idx, TARGET_PAGE_SIZE);
+}
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 03/11] target/rx: CPU definition
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 01/11] target/rx: TCG Translation Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 02/11] target/rx: TCG helper Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 04/11] target/rx: RX disassembler Yoshinori Sato
                     ` (10 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 target/rx/cpu-qom.h |  52 ++++++++++++
 target/rx/cpu.c     | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 target/rx/cpu.h     | 214 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 490 insertions(+)
 create mode 100644 target/rx/cpu-qom.h
 create mode 100644 target/rx/cpu.c
 create mode 100644 target/rx/cpu.h

diff --git a/target/rx/cpu-qom.h b/target/rx/cpu-qom.h
new file mode 100644
index 0000000000..bad6d2c75d
--- /dev/null
+++ b/target/rx/cpu-qom.h
@@ -0,0 +1,52 @@
+/*
+ * QEMU RX CPU
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QEMU_RX_CPU_QOM_H
+#define QEMU_RX_CPU_QOM_H
+
+#include "qom/cpu.h"
+
+#define TYPE_RXCPU "rxcpu"
+
+#define RXCPU_CLASS(klass)                                     \
+    OBJECT_CLASS_CHECK(RXCPUClass, (klass), TYPE_RXCPU)
+#define RXCPU(obj) \
+    OBJECT_CHECK(RXCPU, (obj), TYPE_RXCPU)
+#define RXCPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(RXCPUClass, (obj), TYPE_RXCPU)
+
+/*
+ * RXCPUClass:
+ * @parent_realize: The parent class' realize handler.
+ * @parent_reset: The parent class' reset handler.
+ *
+ * A RX CPU model.
+ */
+typedef struct RXCPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+
+    DeviceRealize parent_realize;
+    void (*parent_reset)(CPUState *cpu);
+
+} RXCPUClass;
+
+typedef struct RXCPU RXCPU;
+
+#endif
diff --git a/target/rx/cpu.c b/target/rx/cpu.c
new file mode 100644
index 0000000000..1d0a2ff03b
--- /dev/null
+++ b/target/rx/cpu.c
@@ -0,0 +1,224 @@
+/*
+ * QEMU RX CPU
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "qemu-common.h"
+#include "migration/vmstate.h"
+#include "exec/exec-all.h"
+#include "hw/loader.h"
+
+static void rx_cpu_set_pc(CPUState *cs, vaddr value)
+{
+    RXCPU *cpu = RXCPU(cs);
+
+    cpu->env.pc = value;
+}
+
+static void rx_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
+{
+    RXCPU *cpu = RXCPU(cs);
+
+    cpu->env.pc = tb->pc;
+}
+
+static bool rx_cpu_has_work(CPUState *cs)
+{
+    return cs->interrupt_request & CPU_INTERRUPT_HARD;
+}
+
+static void rx_cpu_reset(CPUState *s)
+{
+    RXCPU *cpu = RXCPU(s);
+    RXCPUClass *rcc = RXCPU_GET_CLASS(cpu);
+    CPURXState *env = &cpu->env;
+    uint32_t *resetvec;
+
+    rcc->parent_reset(s);
+
+    memset(env, 0, offsetof(CPURXState, end_reset_fields));
+
+    resetvec = rom_ptr(0xfffffffc, 4);
+    if (resetvec) {
+        /* In the case of kernel, it is ignored because it is not set. */
+        env->pc = ldl_p(resetvec);
+    }
+    env->psw = 0x00000000;
+}
+
+typedef struct RXCPUListState {
+    fprintf_function cpu_fprintf;
+    FILE *file;
+} RXCPUListState;
+
+static void rx_cpu_list_entry(gpointer data, gpointer user_data)
+{
+    RXCPUListState *s = user_data;
+    const char *typename = object_class_get_name(OBJECT_CLASS(data));
+    int len = strlen(typename) - strlen(RX_CPU_TYPE_SUFFIX);
+
+    (*s->cpu_fprintf)(s->file, "%.*s\n", len, typename);
+}
+
+void rx_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+    RXCPUListState s = {
+        .cpu_fprintf = cpu_fprintf,
+        .file = f,
+    };
+    GSList *list;
+
+    list = object_class_get_list_sorted(TYPE_RXCPU, false);
+    g_slist_foreach(list, rx_cpu_list_entry, &s);
+    g_slist_free(list);
+}
+
+static ObjectClass *rx_cpu_class_by_name(const char *cpu_model)
+{
+    ObjectClass *oc;
+    char *typename = NULL;
+
+    typename = g_strdup_printf(RX_CPU_TYPE_NAME(""));
+    oc = object_class_by_name(typename);
+    if (oc != NULL && object_class_is_abstract(oc)) {
+        oc = NULL;
+    }
+
+    g_free(typename);
+    return oc;
+}
+
+static void rx_cpu_realize(DeviceState *dev, Error **errp)
+{
+    CPUState *cs = CPU(dev);
+    RXCPUClass *rcc = RXCPU_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    cpu_exec_realizefn(cs, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    cpu_reset(cs);
+    qemu_init_vcpu(cs);
+
+    rcc->parent_realize(dev, errp);
+}
+
+static void rxcpu_set_irq(void *opaque, int no, int request)
+{
+    RXCPU *cpu = opaque;
+    CPUState *cs = CPU(cpu);
+    int irq = request & 0xff;
+
+    static const int mask[] = {
+        [RX_CPU_IRQ] = CPU_INTERRUPT_HARD,
+        [RX_CPU_FIR] = CPU_INTERRUPT_FIR,
+    };
+    if (request & 0x1000) {
+        cpu->env.irq = irq;
+        cpu->env.intlevel = (request >> 8) & 0x0f;
+        cpu_interrupt(cs, mask[no]);
+    } else
+        cpu_reset_interrupt(cs, mask[no]);
+}
+
+static void rx_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
+{
+    info->mach = bfd_mach_rx;
+    info->print_insn = print_insn_rx;
+}
+
+static void rx_cpu_init(Object *obj)
+{
+    CPUState *cs = CPU(obj);
+    RXCPU *cpu = RXCPU(obj);
+    CPURXState *env = &cpu->env;
+
+    cs->env_ptr = env;
+    qdev_init_gpio_in(DEVICE(cpu), rxcpu_set_irq, 2);
+}
+
+static void rxcpu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    CPUClass *cc = CPU_CLASS(klass);
+    RXCPUClass *rcc = RXCPU_CLASS(klass);
+
+    device_class_set_parent_realize(dc, rx_cpu_realize,
+                                    &rcc->parent_realize);
+
+    rcc->parent_reset = cc->reset;
+    cc->reset = rx_cpu_reset;
+
+    cc->class_by_name = rx_cpu_class_by_name;
+    cc->has_work = rx_cpu_has_work;
+    cc->do_interrupt = rx_cpu_do_interrupt;
+    cc->cpu_exec_interrupt = rx_cpu_exec_interrupt;
+    cc->dump_state = rx_cpu_dump_state;
+    cc->set_pc = rx_cpu_set_pc;
+    cc->synchronize_from_tb = rx_cpu_synchronize_from_tb;
+    cc->gdb_read_register = rx_cpu_gdb_read_register;
+    cc->gdb_write_register = rx_cpu_gdb_write_register;
+    cc->get_phys_page_debug = rx_cpu_get_phys_page_debug;
+    cc->disas_set_info = rx_cpu_disas_set_info;
+    cc->tcg_initialize = rx_translate_init;
+
+    cc->gdb_num_core_regs = 26;
+}
+
+static const TypeInfo rxcpu_info = {
+    .name = TYPE_RXCPU,
+    .parent = TYPE_CPU,
+    .instance_size = sizeof(RXCPU),
+    .instance_init = rx_cpu_init,
+    .abstract = false,
+    .class_size = sizeof(RXCPUClass),
+    .class_init = rxcpu_class_init,
+};
+
+static void rxcpu_register_types(void)
+{
+    type_register_static(&rxcpu_info);
+}
+
+type_init(rxcpu_register_types)
+
+static uint32_t extable[32];
+
+void rx_load_image(RXCPU *cpu, const char *filename,
+                   uint32_t start, uint32_t size)
+{
+    long kernel_size;
+    int i;
+
+    kernel_size = load_image_targphys(filename, start, size);
+    if (kernel_size < 0) {
+        fprintf(stderr, "qemu: could not load kernel '%s'\n", filename);
+        exit(1);
+    }
+    cpu->env.pc = start;
+
+    /* setup exception trap trampoline */
+    for (i = 0; i < 32; i++) {
+        extable[i] = 0x10 + i * 4;
+    }
+    rom_add_blob_fixed("extable", extable, sizeof(extable), 0xffffff80);
+}
diff --git a/target/rx/cpu.h b/target/rx/cpu.h
new file mode 100644
index 0000000000..aa4229d76a
--- /dev/null
+++ b/target/rx/cpu.h
@@ -0,0 +1,214 @@
+/*
+ *  RX emulation definition
+ *
+ *  Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RX_CPU_H
+#define RX_CPU_H
+
+#include "qemu-common.h"
+#include "cpu-qom.h"
+
+#define TARGET_LONG_BITS 32
+#define TARGET_PAGE_BITS 12
+
+#define CPUArchState struct CPURXState
+
+#include "exec/cpu-defs.h"
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define PSW_I3 27
+#define PSW_I2 26
+#define PSW_I1 25
+#define PSW_I0 24
+#define PSW_IPL PSW_I0
+#define PSW_PM 20
+#define PSW_U  17
+#define PSW_I  16
+#define PSW_O  3
+#define PSW_S  2
+#define PSW_Z  1
+#define PSW_C  0
+
+#define FPSW_MASK 0xfc007cff
+#define FPSW_RM_MASK 0x00000003
+#define FPSW_DN (1 << 8)
+#define FPSW_CAUSE_MASK 0x000000fc
+#define FPSW_CAUSE_SHIFT 2
+#define FPSW_CAUSE_V (1 << 2)
+#define FPSW_CAUSE_O (1 << 3)
+#define FPSW_CAUSE_Z (1 << 4)
+#define FPSW_CAUSE_U (1 << 5)
+#define FPSW_CAUSE_X (1 << 6)
+#define FPSW_CAUSE_E (1 << 7)
+#define FPSW_ENABLE_MASK 0x00007c00
+#define FPSW_ENABLE_SHIFT 10
+#define FPSW_ENABLE_V (1 << 10)
+#define FPSW_ENABLE_O (1 << 11)
+#define FPSW_ENABLE_Z (1 << 12)
+#define FPSW_ENABLE_U (1 << 13)
+#define FPSW_ENABLE_X (1 << 14)
+#define FPSW_FLAG_SHIFT 26
+#define FPSW_FLAG_V 26
+#define FPSW_FLAG_O 27
+#define FPSW_FLAG_Z 28
+#define FPSW_FLAG_U 29
+#define FPSW_FLAG_X 30
+#define FPSW_FLAG_S 31
+
+#define NB_MMU_MODES 1
+#define MMU_MODE0_SUFFIX _all
+
+#define RX_PSW_OP_NONE 0
+#define RX_PSW_OP_SUB 1
+#define RX_PSW_OP_ADD 2
+#define RX_PSW_OP_SHLL 3
+#define RX_PSW_OP_NEG 4
+
+#define RX_BYTE 0
+#define RX_WORD 1
+#define RX_LONG 2
+
+typedef struct memory_content {
+    uint32_t address;
+    struct memory_content *next;
+} memory_content;
+
+struct CCop;
+
+typedef struct CPURXState {
+    /* CPU registers */
+    uint32_t regs[16];          /* general registers */
+    uint32_t psw;               /* processor status */
+    uint32_t psw_o;             /* O bit of status register */
+    uint32_t psw_s;             /* S bit of status register */
+    uint32_t psw_z;             /* Z bit of status register */
+    uint32_t psw_c;             /* C bit of status register */
+    uint32_t psw_u;
+    uint32_t psw_i;
+    uint32_t psw_pm;
+    uint32_t psw_ipl;
+    uint32_t bpsw;              /* backup status */
+    uint32_t bpc;               /* backup pc */
+    uint32_t isp;               /* global base register */
+    uint32_t usp;               /* vector base register */
+    uint32_t pc;                /* program counter */
+    uint32_t intb;              /* interrupt vector */
+    uint32_t fintv;
+    uint32_t fpsw;
+    uint64_t acc;
+
+    /* Internal use */
+    uint32_t in_sleep;
+    uint32_t intlevel;          /* Requested interrupt level */
+    uint32_t irq;               /* Requested interrupt no (hard) */
+    uint32_t sirq;              /* Requested interrupt no (soft) */
+    float_status fp_status;
+
+    /* Flag operation */
+    uint32_t psw_op;
+    uint32_t psw_v[3];
+    /* Fields up to this point are cleared by a CPU reset */
+    struct {} end_reset_fields;
+
+    CPU_COMMON
+
+    void *ack;
+} CPURXState;
+
+/*
+ * RXCPU:
+ * @env: #CPURXState
+ *
+ * A RX CPU
+ */
+struct RXCPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+
+    CPURXState env;
+};
+
+static inline RXCPU *rx_env_get_cpu(CPURXState *env)
+{
+    return container_of(env, RXCPU, env);
+}
+
+#define ENV_GET_CPU(e) CPU(rx_env_get_cpu(e))
+
+#define ENV_OFFSET offsetof(RXCPU, env)
+
+#define RX_CPU_TYPE_SUFFIX "-" TYPE_RXCPU
+#define RX_CPU_TYPE_NAME(model) model RX_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_RXCPU
+
+void rx_cpu_do_interrupt(CPUState *cpu);
+bool rx_cpu_exec_interrupt(CPUState *cpu, int int_req);
+void rx_cpu_dump_state(CPUState *cpu, FILE *f,
+                           fprintf_function cpu_fprintf, int flags);
+int rx_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
+int rx_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
+hwaddr rx_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+
+void rx_translate_init(void);
+int cpu_rx_signal_handler(int host_signum, void *pinfo,
+                           void *puc);
+
+void rx_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+void rx_load_image(RXCPU *cpu, const char *filename,
+                   uint32_t start, uint32_t size);
+void rx_cpu_pack_psw(CPURXState *env);
+void rx_cpu_unpack_psw(CPURXState *env, int all);
+uint32_t rx_get_psw_low(CPURXState *env);
+uint32_t update_psw_o(CPURXState *env);
+uint32_t psw_cond(CPURXState *env, uint32_t cond);
+
+#define cpu_signal_handler cpu_rx_signal_handler
+#define cpu_list rx_cpu_list
+
+#include "exec/cpu-all.h"
+
+#define CPU_INTERRUPT_SOFT CPU_INTERRUPT_TGT_INT_0
+#define CPU_INTERRUPT_FIR  CPU_INTERRUPT_TGT_INT_1
+
+#define RX_CPU_IRQ 0
+#define RX_CPU_FIR 1
+
+static inline void cpu_get_tb_cpu_state(CPURXState *env, target_ulong *pc,
+                                        target_ulong *cs_base, uint32_t *flags)
+{
+    *pc = env->pc;
+    *cs_base = 0;
+    *flags = 0;
+}
+
+static inline int cpu_mmu_index(CPURXState *env, bool ifetch)
+{
+    return 0;
+}
+
+static inline uint32_t pack_psw(CPURXState *env)
+{
+    return (env->psw_ipl << 24) | (env->psw_pm << 20) |
+        (env->psw_u << 17) | (env->psw_i << 16) |
+        (env->psw_o << 3) | (env->psw_s << 2) |
+        (env->psw_z << 1) | (env->psw_c << 0);
+}
+
+#endif /* RX_CPU_H */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 04/11] target/rx: RX disassembler
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (2 preceding siblings ...)
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 03/11] target/rx: CPU definition Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 05/11] target/rx: miscellaneous functions Yoshinori Sato
                     ` (9 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/disas/bfd.h |    5 +
 target/rx/disas.c   | 1570 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1575 insertions(+)
 create mode 100644 target/rx/disas.c

diff --git a/include/disas/bfd.h b/include/disas/bfd.h
index 41b61c85f9..b2c34274dd 100644
--- a/include/disas/bfd.h
+++ b/include/disas/bfd.h
@@ -228,6 +228,10 @@ enum bfd_architecture
 #define bfd_mach_nios2r2        2
   bfd_arch_lm32,       /* Lattice Mico32 */
 #define bfd_mach_lm32 1
+  bfd_arch_rx,       /* Renesas RX */
+#define bfd_mach_rx            0x75
+#define bfd_mach_rx_v2         0x76
+#define bfd_mach_rx_v3         0x77
   bfd_arch_last
   };
 #define bfd_mach_s390_31 31
@@ -432,6 +436,7 @@ int print_insn_little_nios2     (bfd_vma, disassemble_info*);
 int print_insn_xtensa           (bfd_vma, disassemble_info*);
 int print_insn_riscv32          (bfd_vma, disassemble_info*);
 int print_insn_riscv64          (bfd_vma, disassemble_info*);
+int print_insn_rx(bfd_vma, disassemble_info *);
 
 #if 0
 /* Fetch the disassembler for a given BFD, if that support is available.  */
diff --git a/target/rx/disas.c b/target/rx/disas.c
new file mode 100644
index 0000000000..737fd425b6
--- /dev/null
+++ b/target/rx/disas.c
@@ -0,0 +1,1570 @@
+/*
+ * Renesas RX Disassembler
+ *
+ * Copyright (c) 2019 Yoshinori Sato <ysato@users.sourceforge.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "disas/bfd.h"
+#include "qemu/bitops.h"
+#include "cpu.h"
+
+typedef struct DisasContext {
+    disassemble_info *dis;
+    uint32_t addr;
+} DisasContext;
+
+
+static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn,
+                           int i, int n)
+{
+    bfd_byte buf;
+    while (++i <= n) {
+        ctx->dis->read_memory_func(ctx->addr++, &buf, 1, ctx->dis);
+        insn |= buf << (32 - i * 8);
+    }
+    return insn;
+}
+
+static int32_t li(DisasContext *ctx, int sz)
+{
+    int32_t addr;
+    bfd_byte buf[4];
+    addr = ctx->addr;
+
+    switch (sz) {
+    case 1:
+        ctx->addr += 1;
+        ctx->dis->read_memory_func(addr, buf, 1, ctx->dis);
+        return buf[0];
+    case 2:
+        ctx->addr += 2;
+        ctx->dis->read_memory_func(addr, buf, 2, ctx->dis);
+        return buf[1] << 8 | buf[0];
+    case 3:
+        ctx->addr += 3;
+        ctx->dis->read_memory_func(addr, buf, 3, ctx->dis);
+        return buf[2] << 16 | buf[1] << 8 | buf[0];
+    case 0:
+        ctx->addr += 4;
+        ctx->dis->read_memory_func(addr, buf, 4, ctx->dis);
+        return buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
+    default:
+        g_assert_not_reached();
+    }
+}
+
+/* Include the auto-generated decoder.  */
+#include "decode.inc.c"
+
+#define prt(...) (ctx->dis->fprintf_func)((ctx->dis->stream), __VA_ARGS__)
+
+#define RX_MEMORY_BYTE 0
+#define RX_MEMORY_WORD 1
+#define RX_MEMORY_LONG 2
+
+#define RX_MI_BYTE 0
+#define RX_MI_WORD 1
+#define RX_MI_LONG 2
+#define RX_MI_UWORD 3
+
+static const char size[] = {'b', 'w', 'l'};
+static const char *cond[] = {
+    "eq", "ne", "c", "nc", "gtu", "leu", "pz", "n",
+    "ge", "lt", "gt", "le", "o", "no", "ra", "f"
+};
+static const char *cr[] = {
+    "psw", "", "usp", "fpsw", "", "", "", "",
+    "bpsw", "bpc", "isp", "fintv", "intb", "", "", "",
+};
+static const char *msize[] = {
+    "b", "w", "l", "ub", "uw",
+};
+
+static const char psw[] = {
+    'c', 'z', 's', 'o', 0, 0, 0, 0,
+    'i', 'u', 0, 0, 0, 0, 0, 0,
+};
+
+static uint32_t rx_index_addr(int ld, int size, DisasContext *ctx)
+{
+    bfd_byte buf[2];
+    switch (ld) {
+    case 0:
+        return 0;
+    case 1:
+        ctx->dis->read_memory_func(ctx->addr, buf, 1, ctx->dis);
+        ctx->addr += 1;
+        return buf[0];
+    case 2:
+        ctx->dis->read_memory_func(ctx->addr, buf, 2, ctx->dis);
+        ctx->addr += 2;
+        return buf[1] << 8 | buf[0];
+    }
+    g_assert_not_reached();
+}
+
+static void operand(DisasContext *ctx, int ld, int mi, int rs, int rd)
+{
+    int dsp;
+    const char *mis;
+    static const char *sizes[] = {".b", ".w", ".l"};
+    if (ld < 3) {
+        switch (mi) {
+        case 4:
+            /* dsp[rs].ub */
+            dsp = rx_index_addr(ld, RX_MEMORY_BYTE, ctx);
+            mis = ".ub";
+            break;
+        case 3:
+            /* dsp[rs].uw */
+            dsp = rx_index_addr(ld, RX_MEMORY_BYTE, ctx);
+            mis = ".uw";
+            break;
+        default:
+            dsp = rx_index_addr(ld, mi, ctx);
+            mis = sizes[mi];
+            break;
+        }
+        if (dsp > 0) {
+            dsp <<= ld;
+            prt("%d", dsp);
+        }
+        prt("[r%d]%s", rs, mis);
+    } else {
+        prt("r%d", rs);
+    }
+    prt(",r%d", rd);
+}
+
+/* mov.[bwl] rs,dsp:[rd] */
+static bool trans_MOV_mr(DisasContext *ctx, arg_MOV_mr *a)
+{
+    if (a->dsp > 0) {
+        prt("mov.%c\tr%d,%d[r%d]",
+            size[a->sz], a->rs, a->dsp << a->sz, a->rd);
+    } else {
+        prt("mov.%c\tr%d,[r%d]",
+            size[a->sz], a->rs, a->rd);
+    }
+    return true;
+}
+
+/* mov.[bwl] dsp:[rd],rs */
+static bool trans_MOV_rm(DisasContext *ctx, arg_MOV_rm *a)
+{
+    if (a->dsp > 0) {
+        prt("mov.%c\t%d[r%d],r%d",
+            size[a->sz], a->dsp << a->sz, a->rd, a->rs);
+    } else {
+        prt("mov.%c\t[r%d],r%d",
+            size[a->sz], a->rd, a->rs);
+    }
+    return true;
+}
+
+/* mov.l #uimm4,rd */
+/* mov.l #uimm8,rd */
+static bool trans_MOV_ri(DisasContext *ctx, arg_MOV_ri *a)
+{
+    prt("mov.l\t#%d,r%d", a->imm & 0xff, a->rd);
+    return true;
+}
+
+/* mov.[bwl] #uimm8,dsp:[rd] */
+static bool trans_MOV_mi(DisasContext *ctx, arg_MOV_mi *a)
+{
+    if (a->dsp > 0) {
+        prt("mov.%c\t#%d,%d[r%d]",
+            size[a->sz], a->imm & 0xff, a->dsp << a->sz, a->rd);
+    } else {
+        prt("mov.%c\t#%d,[r%d]",
+            size[a->sz], a->imm & 0xff, a->rd);
+    }
+    return true;
+}
+
+/* mov.l #imm,rd */
+static bool trans_MOV_rli(DisasContext *ctx, arg_MOV_rli *a)
+{
+    prt("mov.l\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+
+/* mov #imm, dsp:[rd] */
+static bool trans_MOV_mli(DisasContext *ctx, arg_MOV_mli *a)
+{
+    if (a->ld == 2) {
+        a->dsp = bswap_16(a->dsp);
+    }
+    if (a->dsp > 0) {
+        prt("mov.%c\t#0x%08x,%d[r%d]",
+            size[a->sz], a->imm, a->dsp << a->sz, a->rd);
+    } else {
+        prt("mov.%c\t#0x%08x,[r%d]",
+            size[a->sz], a->imm, a->rd);
+    }
+    return true;
+}
+
+
+/* mov.[bwl] [ri,rb],rd */
+static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a)
+{
+    prt("mov.%c\t[r%d,r%d],r%d", size[a->sz], a->ri, a->rb, a->rd);
+    return true;
+}
+
+/* mov.[bwl] rd,[ri,rb] */
+static bool trans_MOV_ar(DisasContext *ctx, arg_MOV_ar *a)
+{
+    prt("mov.%c\tr%d,[r%d,r%d]", size[a->sz], a->rs, a->ri, a->rb);
+    return true;
+}
+
+
+/* mov.[bwl] dsp:[rs],dsp:[rd] */
+/* mov.[bwl] rs,dsp:[rd] */
+/* mov.[bwl] dsp:[rs],rd */
+/* mov.[bwl] rs,rd */
+static bool trans_MOV_ll(DisasContext *ctx, arg_MOV_ll *a)
+{
+    int rs, rd, dsp;
+
+    if (a->lds == 3 && a->ldd < 3) {
+        rs = a->rd;
+        rd = a->rs;
+    } else {
+        rs = a->rs;
+        rd = a->rd;
+    }
+    prt("mov.%c\t", size[a->sz]);
+    if (a->lds < 3) {
+        dsp = rx_index_addr(a->lds, a->sz, ctx);
+        if (dsp > 0) {
+            prt("%d", dsp);
+        }
+        prt("[r%d],", rs);
+    } else {
+        prt("r%d,", rs);
+    }
+    if (a->ldd < 3) {
+        dsp = rx_index_addr(a->ldd, a->sz, ctx);
+        if (dsp > 0) {
+            prt("%d", dsp);
+        }
+        prt("[r%d]", rd);
+    } else {
+        prt("r%d", rd);
+    }
+    return true;
+}
+
+/* mov.[bwl] rs,[rd+] */
+/* mov.[bwl] rs,[-rd] */
+static bool trans_MOV_pr(DisasContext *ctx, arg_MOV_pr *a)
+{
+    prt("mov.%c\tr%d,", size[a->sz], a->rs);
+    if (a->ad == 0) {
+        prt("[r%d+]", a->rd);
+    } else {
+        prt("[-r%d]", a->rd);
+    }
+    return true;
+}
+
+/* mov.[bwl] [rd+],rs */
+/* mov.[bwl] [-rd],rs */
+static bool trans_MOV_rp(DisasContext *ctx, arg_MOV_rp *a)
+{
+    prt("mov.%c\t", size[a->sz]);
+    if (a->ad == 1) {
+        prt("[-r%d]", a->rd);
+    } else {
+        prt("[r%d+]", a->rd);
+    }
+    prt(",r%d", a->rs);
+    return true;
+}
+
+/* movu.[bw] dsp5:[rs],rd */
+static bool trans_MOVU_rm(DisasContext *ctx, arg_MOVU_rm *a)
+{
+    if (a->dsp > 0) {
+        prt("movu.%c%d[r%d],r%d", size[a->sz], a->dsp << a->sz, a->rs, a->rd);
+    } else {
+        prt("movu.%c[r%d],r%d", size[a->sz], a->rs, a->rd);
+    }
+    return true;
+}
+
+/* movu.[bw] rs,rd */
+/* movu.[bw] dsp:[rs],rd */
+static bool trans_MOVU_rl(DisasContext *ctx, arg_MOVU_rl *a)
+{
+    int dsp;
+    prt("movu.%c\t", size[a->sz]);
+    if (a->ld < 3) {
+        /* from memory */
+        dsp = rx_index_addr(a->ld, a->sz, ctx);
+        if (dsp > 0) {
+            prt("%d", dsp);
+        }
+        prt("[r%d]", a->rs);
+    } else {
+        prt("r%d", a->rs);
+    }
+    prt(",r%d", a->rd);
+    return true;
+}
+
+/* movu.[bw] [ri,rb],rd */
+static bool trans_MOVU_ra(DisasContext *ctx, arg_MOVU_ra *a)
+{
+    prt("mov.%c\t[r%d,r%d],r%d", size[a->sz], a->ri, a->rb, a->rd);
+    return true;
+}
+
+/* movu.[bw] [rs+],rd */
+/* movu.[bw] [-rs],rd */
+static bool trans_MOVU_rp(DisasContext *ctx, arg_MOVU_rp *a)
+{
+    prt("movu.%c\t", size[a->sz]);
+    if (a->ad == 1) {
+        prt("[-r%d]", a->rd);
+    } else {
+        prt("[r%d+]", a->rd);
+    }
+    prt(",r%d", a->rs);
+    return true;
+}
+
+/* pop rd */
+static bool trans_POP(DisasContext *ctx, arg_POP *a)
+{
+    prt("pop\tr%d", a->rd);
+    return true;
+}
+
+/* popc rx */
+static bool trans_POPC(DisasContext *ctx, arg_POPC *a)
+{
+    prt("pop\tr%s", cr[a->cr]);
+    return true;
+}
+
+/* popm rd-rd2 */
+static bool trans_POPM(DisasContext *ctx, arg_POPM *a)
+{
+    prt("popm\tr%d-r%d", a->rd, a->rd2);
+    return true;
+}
+
+/* push rs */
+static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a)
+{
+    prt("push\tr%d", a->rs);
+    return true;
+}
+
+/* push dsp[rs] */
+static bool trans_PUSH_m(DisasContext *ctx, arg_PUSH_m *a)
+{
+    prt("push\t");
+    int dsp = rx_index_addr(a->ld, a->sz, ctx);
+    if (dsp > 0) {
+        prt("%d", dsp);
+    }
+    prt("[r%d]", a->rs);
+    return true;
+}
+
+/* pushc rx */
+static bool trans_PUSHC(DisasContext *ctx, arg_PUSHC *a)
+{
+    prt("push\t%s", cr[a->cr]);
+    return true;
+}
+
+/* pushm rs-rs2*/
+static bool trans_PUSHM(DisasContext *ctx, arg_PUSHM *a)
+{
+    prt("pushm\tr%d-r%d", a->rs, a->rs2);
+    return true;
+}
+
+/* xchg rs,rd */
+/* xchg dsp[rs].<mi>,rd */
+static bool trans_XCHG_rl(DisasContext *ctx, arg_XCHG_rl *a)
+{
+    int dsp;
+
+    prt("xchg\t");
+    if (a->ld == 3) {
+        /* xchg rs,rd */
+        prt("r%d", a->rs);
+    } else {
+        dsp = rx_index_addr(a->ld, a->mi, ctx);
+        if (dsp > 0) {
+            prt("%d", dsp);
+        }
+        prt("[r%d].%s", a->rs, msize[a->mi]);
+    }
+    prt(",r%d", a->rd);
+    return true;
+}
+
+/* stz #imm,rd */
+static bool trans_STZ(DisasContext *ctx, arg_STZ *a)
+{
+    prt("stz\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* stnz #imm,rd */
+static bool trans_STNZ(DisasContext *ctx, arg_STNZ *a)
+{
+    prt("stnz\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* rtsd #imm */
+static bool trans_RTSD_i(DisasContext *ctx, arg_RTSD_i *a)
+{
+    prt("rtsd\t#%d", (a->imm & 0xff) << 2);
+    return true;
+}
+
+/* rtsd #imm, rd-rd2 */
+static bool trans_RTSD_irr(DisasContext *ctx, arg_RTSD_irr *a)
+{
+    prt("rtsd\t#%d,r%d-r%d", (a->imm & 0xff) << 2, a->rd, a->rd2);
+    return true;
+}
+
+/* and #uimm:4, rd */
+static bool trans_AND_ri(DisasContext *ctx, arg_AND_ri *a)
+{
+    prt("and\t#%d,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* and #imm, rd */
+static bool trans_AND_rli(DisasContext *ctx, arg_AND_rli *a)
+{
+    prt("and\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* and dsp[rs], rd */
+/* and rs,rd */
+static bool trans_AND_rl(DisasContext *ctx, arg_AND_rl *a)
+{
+    prt("and\t");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* and rs,rs2,rd */
+static bool trans_AND_rrr(DisasContext *ctx, arg_AND_rrr *a)
+{
+    prt("and\tr%d,r%d,r%d", a->rs, a->rs2, a->rd);
+    return true;
+}
+
+/* or #uimm:4, rd */
+static bool trans_OR_ri(DisasContext *ctx, arg_OR_ri *a)
+{
+    prt("or\t#%d,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* or #imm, rd */
+static bool trans_OR_rli(DisasContext *ctx, arg_OR_rli *a)
+{
+    prt("or\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* or dsp[rs], rd */
+/* or rs,rd */
+static bool trans_OR_rl(DisasContext *ctx, arg_OR_rl *a)
+{
+    prt("or\t");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* or rs,rs2,rd */
+static bool trans_OR_rrr(DisasContext *ctx, arg_OR_rrr *a)
+{
+    prt("or\tr%d,r%d,r%d", a->rs, a->rs2, a->rd);
+    return true;
+}
+
+/* xor #imm, rd */
+static bool trans_XOR_rli(DisasContext *ctx, arg_XOR_rli *a)
+{
+    prt("xor\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* xor dsp[rs], rd */
+/* xor rs,rd */
+static bool trans_XOR_rl(DisasContext *ctx, arg_XOR_rl *a)
+{
+    prt("xor\t");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* tst #imm, rd */
+static bool trans_TST_rli(DisasContext *ctx, arg_TST_rli *a)
+{
+    prt("tst\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* tst dsp[rs], rd */
+/* tst rs, rd */
+static bool trans_TST_rl(DisasContext *ctx, arg_TST_rl *a)
+{
+    prt("tst\t");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* not rd */
+/* not rs, rd */
+static bool trans_NOT_rr(DisasContext *ctx, arg_NOT_rr *a)
+{
+    prt("not\t");
+    if (a->rs < 16) {
+        prt("r%d", a->rs);
+    }
+    prt("r%d", a->rd);
+    return true;
+}
+
+/* neg rd */
+/* neg rs, rd */
+static bool trans_NEG_rr(DisasContext *ctx, arg_NEG_rr *a)
+{
+    prt("neg\t");
+    if (a->rs < 16) {
+        prt("r%d", a->rs);
+    }
+    prt("r%d", a->rd);
+    return true;
+}
+
+/* adc #imm, rd */
+static bool trans_ADC_rli(DisasContext *ctx, arg_ADC_rli *a)
+{
+    prt("adc\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* adc rs, rd */
+static bool trans_ADC_rr(DisasContext *ctx, arg_ADC_rr *a)
+{
+    prt("adc\tr%d,r%d", a->rs, a->rd);
+    return true;
+}
+
+/* adc dsp[rs], rd */
+static bool trans_ADC_rl(DisasContext *ctx, arg_ADC_rl *a)
+{
+    int dsp;
+    prt("adc\t");
+    dsp = rx_index_addr(a->ld, RX_LONG, ctx);
+    if (dsp > 0) {
+        prt("%d", dsp);
+    }
+    prt("[r%d],r%d", a->rs, a->rd);
+    return true;
+}
+
+/* add #uimm4, rd */
+static bool trans_ADD_rri(DisasContext *ctx, arg_ADD_rri *a)
+{
+    prt("add\t#%d,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* add rs, rd */
+/* add dsp[rs], rd */
+static bool trans_ADD_rl(DisasContext *ctx, arg_ADD_rl *a)
+{
+    prt("add\t");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* add #imm, rs, rd */
+static bool trans_ADD_rrli(DisasContext *ctx, arg_ADD_rrli *a)
+{
+    prt("add\t#0x%08x,r%d,r%d", a->imm, a->rs2, a->rd);
+    return true;
+}
+
+/* add rs, rs2, rd */
+static bool trans_ADD_rrr(DisasContext *ctx, arg_ADD_rrr *a)
+{
+    prt("add\tr%d,r%d,r%d", a->rs, a->rs2, a->rd);
+    return true;
+}
+
+/* cmp #imm4, rd */
+/* cmp #imm8, rd */
+static bool trans_CMP_ri(DisasContext *ctx, arg_CMP_ri *a)
+{
+    int rs;
+    rs = (a->rs2 < 16) ? a->rs2 : a->rd;
+    prt("cmp\t#%d,r%d", a->imm & 0xff, rs);
+    return true;
+}
+
+/* cmp #imm, rs2 */
+static bool trans_CMP_rli(DisasContext *ctx, arg_CMP_rli *a)
+{
+    prt("cmp\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* cmp rs, rs2 */
+/* cmp dsp[rs], rs2 */
+static bool trans_CMP_rl(DisasContext *ctx, arg_CMP_rl *a)
+{
+    prt("cmp\t");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* sub #imm4, rd */
+static bool trans_SUB_ri(DisasContext *ctx, arg_SUB_ri *a)
+{
+    prt("sub\t#%d,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* sub rs, rd */
+/* sub dsp[rs], rd */
+static bool trans_SUB_rl(DisasContext *ctx, arg_SUB_rl *a)
+{
+    prt("sub\t");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* sub rs, rs2, rd */
+static bool trans_SUB_rrr(DisasContext *ctx, arg_SUB_rrr *a)
+{
+    prt("sub\tr%d,r%d,r%d", a->rs, a->rs2, a->rd);
+    return true;
+}
+
+/* sbb rs, rd */
+static bool trans_SBB_rr(DisasContext *ctx, arg_SBB_rr *a)
+{
+    prt("sbb\tr%d,%d", a->rs, a->rd);
+    return true;
+}
+
+/* sbb dsp[rs], rd */
+static bool trans_SBB_rl(DisasContext *ctx, arg_SBB_rl *a)
+{
+    prt("sbb\t");
+    operand(ctx, a->ld, RX_MI_LONG, a->rs, a->rd);
+    return true;
+}
+
+/* abs rd */
+/* abs rs, rd */
+static bool trans_ABS_rr(DisasContext *ctx, arg_ABS_rr *a)
+{
+    prt("abs\t");
+    if (a->rs < 16) {
+        prt("r%d,r%d", a->rs, a->rd);
+    } else {
+        prt("r%d", a->rd);
+    }
+    return true;
+}
+
+/* max #imm, rd */
+static bool trans_MAX_ri(DisasContext *ctx, arg_MAX_ri *a)
+{
+    prt("max\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* max rs, rd */
+/* max dsp[rs], rd */
+static bool trans_MAX_rl(DisasContext *ctx, arg_MAX_rl *a)
+{
+    prt("max\t");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* min #imm, rd */
+static bool trans_MIN_ri(DisasContext *ctx, arg_MIN_ri *a)
+{
+    prt("min\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* min rs, rd */
+/* min dsp[rs], rd */
+static bool trans_MIN_rl(DisasContext *ctx, arg_MIN_rl *a)
+{
+    prt("max\t");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* mul #uimm4, rd */
+static bool trans_MUL_ri(DisasContext *ctx, arg_MUL_ri *a)
+{
+    prt("mul\t#%d,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* mul #imm, rd */
+static bool trans_MUL_rli(DisasContext *ctx, arg_MUL_rli *a)
+{
+    prt("mul\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* mul rs, rd */
+/* mul dsp[rs], rd */
+static bool trans_MUL_rl(DisasContext *ctx, arg_MUL_rl *a)
+{
+    prt("mul\t");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* mul rs, rs2, rd */
+static bool trans_MUL_rrr(DisasContext *ctx, arg_MUL_rrr *a)
+{
+    prt("mul\tr%d,r%d,r%d", a->rs, a->rs2, a->rd);
+    return true;
+}
+
+/* emul #imm, rd */
+static bool trans_EMUL_ri(DisasContext *ctx, arg_EMUL_ri *a)
+{
+    prt("emul\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* emul rs, rd */
+/* emul dsp[rs], rd */
+static bool trans_EMUL_rl(DisasContext *ctx, arg_EMUL_rl *a)
+{
+    prt("emul\n");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* emulu #imm, rd */
+static bool trans_EMULU_ri(DisasContext *ctx, arg_EMULU_ri *a)
+{
+    prt("emulu\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* emulu rs, rd */
+/* emulu dsp[rs], rd */
+static bool trans_EMULU_rl(DisasContext *ctx, arg_EMULU_rl *a)
+{
+    prt("emulu\n");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* div #imm, rd */
+static bool trans_DIV_ri(DisasContext *ctx, arg_DIV_ri *a)
+{
+    prt("div\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* div rs, rd */
+/* div dsp[rs], rd */
+static bool trans_DIV_rl(DisasContext *ctx, arg_DIV_rl *a)
+{
+    prt("div\n");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+/* divu #imm, rd */
+static bool trans_DIVU_ri(DisasContext *ctx, arg_DIVU_ri *a)
+{
+    prt("divu\t#0x%08x,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* divu rs, rd */
+/* divu dsp[rs], rd */
+static bool trans_DIVU_rl(DisasContext *ctx, arg_DIVU_rl *a)
+{
+    prt("divu\n");
+    operand(ctx, a->ld, a->mi, a->rs, a->rd);
+    return true;
+}
+
+
+/* shll #imm:5, rd */
+/* shll #imm:5, rs, rd */
+static bool trans_SHLL_rri(DisasContext *ctx, arg_SHLL_rri *a)
+{
+    prt("shll\t#%d,", a->imm);
+    if (a->rs2 < 16) {
+        prt("r%d,", a->rs2);
+    }
+    prt("r%d", a->rd);
+    return true;
+}
+
+/* shll rs, rd */
+static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a)
+{
+    prt("shll\tr%d,r%d", a->rs, a->rd);
+    return true;
+}
+
+/* shar #imm:5, rd */
+/* shar #imm:5, rs, rd */
+static bool trans_SHAR_rri(DisasContext *ctx, arg_SHAR_rri *a)
+{
+    prt("shar\t#%d,", a->imm);
+    if (a->rs2 < 16) {
+        prt("r%d,", a->rs2);
+    }
+    prt("r%d", a->rd);
+    return true;
+}
+
+/* shar rs, rd */
+static bool trans_SHAR_rr(DisasContext *ctx, arg_SHAR_rr *a)
+{
+    prt("shar\tr%d,r%d", a->rs, a->rd);
+    return true;
+}
+
+/* shlr #imm:5, rd */
+/* shlr #imm:5, rs, rd */
+static bool trans_SHLR_rri(DisasContext *ctx, arg_SHLR_rri *a)
+{
+    prt("shlr\t#%d,", a->imm);
+    if (a->rs2 < 16) {
+        prt("r%d,", a->rs2);
+    }
+    prt("r%d", a->rd);
+    return true;
+}
+
+/* shlr rs, rd */
+static bool trans_SHLR_rr(DisasContext *ctx, arg_SHLR_rr *a)
+{
+    prt("shlr\tr%d,r%d", a->rs, a->rd);
+    return true;
+}
+
+/* rolc rd */
+static bool trans_ROLC(DisasContext *ctx, arg_ROLC *a)
+{
+    prt("rorc\tr%d", a->rd);
+    return true;
+}
+
+/* rorc rd */
+static bool trans_RORC(DisasContext *ctx, arg_RORC *a)
+{
+    prt("rorc\tr%d", a->rd);
+    return true;
+}
+
+/* rotl #imm, rd */
+static bool trans_ROTL_ri(DisasContext *ctx, arg_ROTL_ri *a)
+{
+    prt("rotl\t#%d,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* rotl rs, rd */
+static bool trans_ROTL_rr(DisasContext *ctx, arg_ROTL_rr *a)
+{
+    prt("rotl\tr%d,r%d", a->rs, a->rd);
+    return true;
+}
+
+/* rotr #imm, rd */
+static bool trans_ROTR_ri(DisasContext *ctx, arg_ROTR_ri *a)
+{
+    prt("rotr\t#%d,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* rotr rs, rd */
+static bool trans_ROTR_rr(DisasContext *ctx, arg_ROTR_rr *a)
+{
+    prt("rotr\tr%d,r%d", a->rs, a->rd);
+    return true;
+}
+
+/* revl rs, rd */
+static bool trans_REVL(DisasContext *ctx, arg_REVL *a)
+{
+    prt("revl\tr%d,r%d", a->rs, a->rd);
+    return true;
+}
+
+/* revw rs, rd */
+static bool trans_REVW(DisasContext *ctx, arg_REVW *a)
+{
+    prt("revw\tr%d,r%d", a->rs, a->rd);
+    return true;
+}
+
+/* conditional branch helper */
+static void rx_bcnd_main(DisasContext *ctx, int cd, int dst, int len)
+{
+    static const char sz[] = {'s', 'b', 'w', 'a'};
+    prt("b%s.%c\t%08x", cond[cd], sz[len - 1], ctx->addr - len + dst);
+}
+
+static int16_t rev16(uint16_t dsp)
+{
+    return ((dsp << 8) & 0xff00) | ((dsp >> 8) & 0x00ff);
+}
+
+static int32_t rev24(uint32_t dsp)
+{
+    dsp = ((dsp << 16) & 0xff0000) |
+        (dsp & 0x00ff00) |
+        ((dsp >> 16) & 0x0000ff);
+    dsp |= (dsp & 0x00800000) ? 0xff000000 : 0x00000000;
+    return dsp;
+}
+
+/* beq dsp:3 */
+/* bne dsp:3 */
+static bool trans_BCnd_s(DisasContext *ctx, arg_BCnd_s *a)
+{
+    if (a->dsp < 3) {
+        a->dsp += 8;
+    }
+    rx_bcnd_main(ctx, a->cd, a->dsp, 1);
+    return true;
+}
+
+/* beq dsp:8 */
+/* bne dsp:8 */
+/* bc dsp:8 */
+/* bnc dsp:8 */
+/* bgtu dsp:8 */
+/* bleu dsp:8 */
+/* bpz dsp:8 */
+/* bn dsp:8 */
+/* bge dsp:8 */
+/* blt dsp:8 */
+/* bgt dsp:8 */
+/* ble dsp:8 */
+/* bo dsp:8 */
+/* bno dsp:8 */
+/* bra dsp:8 */
+static bool trans_BCnd_b(DisasContext *ctx, arg_BCnd_b *a)
+{
+    rx_bcnd_main(ctx, a->cd, (int8_t)a->dsp, 2);
+    return true;
+}
+
+/* beq dsp:16 */
+/* bne dsp:16 */
+static bool trans_BCnd_w(DisasContext *ctx, arg_BCnd_w *a)
+{
+    rx_bcnd_main(ctx, a->cd, rev16(a->dsp), 3);
+    return true;
+}
+
+/* bra dsp:3 */
+static bool trans_BRA_s(DisasContext *ctx, arg_BRA_s *a)
+{
+    if (a->dsp < 3) {
+        a->dsp += 8;
+    }
+    rx_bcnd_main(ctx, 14, a->dsp, 1);
+    return true;
+}
+
+/* bra dsp:16 */
+static bool trans_BRA_w(DisasContext *ctx, arg_BRA_w *a)
+{
+    rx_bcnd_main(ctx, 14, rev16(a->dsp), 3);
+    return true;
+}
+
+/* bra dsp:24 */
+static bool trans_BRA_a(DisasContext *ctx, arg_BRA_a *a)
+{
+    rx_bcnd_main(ctx, 14, rev24(a->dsp), 4);
+    return true;
+}
+
+/* bra rs */
+static bool trans_BRA_l(DisasContext *ctx, arg_BRA_l *a)
+{
+    prt("bra.l\tr%d", a->rd);
+    return true;
+}
+
+/* jmp rs */
+static bool trans_JMP(DisasContext *ctx, arg_JMP *a)
+{
+    prt("jmp\tr%d", a->rs);
+    return true;
+}
+
+/* jsr rs */
+static bool trans_JSR(DisasContext *ctx, arg_JSR *a)
+{
+    prt("jsr\tr%d", a->rs);
+    return true;
+}
+
+/* bsr dsp:16 */
+static bool trans_BSR_w(DisasContext *ctx, arg_BSR_w *a)
+{
+    prt("bsr.w\t%08x", ctx->addr - 3 + rev16(a->dsp));
+    return true;
+}
+
+/* bsr dsp:24 */
+static bool trans_BSR_a(DisasContext *ctx, arg_BSR_a *a)
+{
+    prt("bsr.a\t%08x", ctx->addr - 4 + rev24(a->dsp));
+    return true;
+}
+
+/* bsr rs */
+static bool trans_BSR_l(DisasContext *ctx, arg_BSR_l *a)
+{
+    prt("bsr.l\tr%d", a->rd);
+    return true;
+}
+
+/* rts */
+static bool trans_RTS(DisasContext *ctx, arg_RTS *a)
+{
+    prt("rts");
+    return true;
+}
+
+/* nop */
+static bool trans_NOP(DisasContext *ctx, arg_NOP *a)
+{
+    prt("nop");
+    return true;
+}
+
+/* scmpu */
+static bool trans_SCMPU(DisasContext *ctx, arg_SCMPU *a)
+{
+    prt("scmpu");
+    return true;
+}
+
+/* smovu */
+static bool trans_SMOVU(DisasContext *ctx, arg_SMOVU *a)
+{
+    prt("smovu");
+    return true;
+}
+
+/* smovf */
+static bool trans_SMOVF(DisasContext *ctx, arg_SMOVF *a)
+{
+    prt("smovf");
+    return true;
+}
+
+/* smovb */
+static bool trans_SMOVB(DisasContext *ctx, arg_SMOVB *a)
+{
+    prt("smovb");
+    return true;
+}
+
+/* suntile */
+static bool trans_SUNTIL(DisasContext *ctx, arg_SUNTIL *a)
+{
+    prt("suntil.%c", size[a->sz]);
+    return true;
+}
+
+/* swhile */
+static bool trans_SWHILE(DisasContext *ctx, arg_SWHILE *a)
+{
+    prt("swhile.%c", size[a->sz]);
+    return true;
+}
+/* sstr */
+static bool trans_SSTR(DisasContext *ctx, arg_SSTR *a)
+{
+    prt("sstr.%c", size[a->sz]);
+    return true;
+}
+
+/* rmpa */
+static bool trans_RMPA(DisasContext *ctx, arg_RMPA *a)
+{
+    prt("rmpa.%c", size[a->sz]);
+    return true;
+}
+
+/* mulhi rs,rs2 */
+static bool trans_MULHI(DisasContext *ctx, arg_MULHI *a)
+{
+    prt("mulhi\tr%d,r%d", a->rs, a->rs2);
+    return true;
+}
+
+/* mullo rs,rs2 */
+static bool trans_MULLO(DisasContext *ctx, arg_MULLO *a)
+{
+    prt("mullo\tr%d,r%d", a->rs, a->rs2);
+    return true;
+}
+
+/* machi rs,rs2 */
+static bool trans_MACHI(DisasContext *ctx, arg_MACHI *a)
+{
+    prt("machi\tr%d,r%d", a->rs, a->rs2);
+    return true;
+}
+
+/* maclo rs,rs2 */
+static bool trans_MACLO(DisasContext *ctx, arg_MACLO *a)
+{
+    prt("maclo\tr%d,r%d", a->rs, a->rs2);
+    return true;
+}
+
+/* mvfachi rd */
+static bool trans_MVFACHI(DisasContext *ctx, arg_MVFACHI *a)
+{
+    prt("mvfachi\tr%d", a->rd);
+    return true;
+}
+
+/* mvfacmi rd */
+static bool trans_MVFACMI(DisasContext *ctx, arg_MVFACMI *a)
+{
+    prt("mvfacmi\tr%d", a->rd);
+    return true;
+}
+
+/* mvtachi rs */
+static bool trans_MVTACHI(DisasContext *ctx, arg_MVTACHI *a)
+{
+    prt("mvtachi\tr%d", a->rs);
+    return true;
+}
+
+/* mvtaclo rs */
+static bool trans_MVTACLO(DisasContext *ctx, arg_MVTACLO *a)
+{
+    prt("mvtaclo\tr%d", a->rs);
+    return true;
+}
+
+/* racw #imm */
+static bool trans_RACW(DisasContext *ctx, arg_RACW *a)
+{
+    prt("racw\t#%d", a->imm + 1);
+    return true;
+}
+
+/* sat rd */
+static bool trans_SAT(DisasContext *ctx, arg_SAT *a)
+{
+    prt("sat\tr%d", a->rd);
+    return true;
+}
+
+/* satr */
+static bool trans_SATR(DisasContext *ctx, arg_SATR *a)
+{
+    prt("satr");
+    return true;
+}
+
+/* fadd #imm, rd */
+static bool trans_FADD_ri(DisasContext *ctx, arg_FADD_ri *a)
+{
+    prt("fadd\t#%d,r%d", li(ctx, 0), a->rd);
+    return true;
+}
+
+/* fadd dsp[rs], rd */
+/* fadd rs, rd */
+static bool trans_FADD_rl(DisasContext *ctx, arg_FADD_rl *a)
+{
+    prt("fadd\t");
+    operand(ctx, a->ld, RX_MI_LONG, a->rs, a->rd);
+    return true;
+}
+
+/* fcmp #imm, rd */
+static bool trans_FCMP_ri(DisasContext *ctx, arg_FCMP_ri *a)
+{
+    prt("fadd\t#%d,r%d", li(ctx, 0), a->rd);
+    return true;
+}
+
+/* fcmp dsp[rs], rd */
+/* fcmp rs, rd */
+static bool trans_FCMP_rl(DisasContext *ctx, arg_FCMP_rl *a)
+{
+    prt("fcmp\t");
+    operand(ctx, a->ld, RX_MI_LONG, a->rs, a->rd);
+    return true;
+}
+
+/* fsub #imm, rd */
+static bool trans_FSUB_ri(DisasContext *ctx, arg_FSUB_ri *a)
+{
+    prt("fsub\t#%d,r%d", li(ctx, 0), a->rd);
+    return true;
+}
+
+/* fsub dsp[rs], rd */
+/* fsub rs, rd */
+static bool trans_FSUB_rl(DisasContext *ctx, arg_FSUB_rl *a)
+{
+    prt("fsub\t");
+    operand(ctx, a->ld, RX_MI_LONG, a->rs, a->rd);
+    return true;
+}
+
+/* ftoi dsp[rs], rd */
+/* ftoi rs, rd */
+static bool trans_FTOI(DisasContext *ctx, arg_FTOI *a)
+{
+    prt("ftoi\t");
+    operand(ctx, a->ld, RX_MI_LONG, a->rs, a->rd);
+    return true;
+}
+
+/* fmul #imm, rd */
+static bool trans_FMUL_ri(DisasContext *ctx, arg_FMUL_ri *a)
+{
+    prt("fmul\t#%d,r%d", li(ctx, 0), a->rd);
+    return true;
+}
+
+/* fmul dsp[rs], rd */
+/* fmul rs, rd */
+static bool trans_FMUL_rl(DisasContext *ctx, arg_FMUL_rl *a)
+{
+    prt("fmul\t");
+    operand(ctx, a->ld, RX_MI_LONG, a->rs, a->rd);
+    return true;
+}
+
+/* fdiv #imm, rd */
+static bool trans_FDIV_ri(DisasContext *ctx, arg_FDIV_ri *a)
+{
+    prt("fdiv\t#%d,r%d", li(ctx, 0), a->rd);
+    return true;
+}
+
+/* fdiv dsp[rs], rd */
+/* fdiv rs, rd */
+static bool trans_FDIV_rl(DisasContext *ctx, arg_FDIV_rl *a)
+{
+    prt("fdiv\t");
+    operand(ctx, a->ld, RX_MI_LONG, a->rs, a->rd);
+    return true;
+}
+
+/* round dsp[rs], rd */
+/* round rs, rd */
+static bool trans_ROUND(DisasContext *ctx, arg_ROUND *a)
+{
+    prt("round\t");
+    operand(ctx, a->ld, RX_MI_LONG, a->rs, a->rd);
+    return true;
+}
+
+/* itof rs, rd */
+/* itof dsp[rs], rd */
+static bool trans_ITOF(DisasContext *ctx, arg_ITOF *a)
+{
+    prt("itof\t");
+    operand(ctx, a->ld, RX_MI_LONG, a->rs, a->rd);
+    return true;
+}
+
+#define BOP_IM(name, reg)                           \
+    do {                                            \
+        int dsp;                                    \
+        prt("b%s\t#%d,", #name, a->imm);             \
+        dsp = rx_index_addr(a->ld, RX_MEMORY_BYTE, ctx);        \
+        if (dsp > 0) {                                          \
+            prt("%d", dsp);                                     \
+        }                                                       \
+        prt("[r%d]", reg);                                      \
+        return true;                                            \
+    } while (0)
+
+/* bset #imm, dsp[rd] */
+static bool trans_BSET_li(DisasContext *ctx, arg_BSET_li *a)
+{
+    BOP_IM(bset, a->rs);
+}
+
+#define BOP_RM(name) \
+    do {             \
+        int dsp;               \
+        prt("b%s\tr%d,", #name, a->rs2);        \
+        switch (a->ld) {                        \
+        case 0 ... 2:                                    \
+            dsp = rx_index_addr(a->ld, RX_MEMORY_BYTE, ctx);    \
+            if (dsp > 0) {                                      \
+                prt("%d", dsp);                                 \
+            }                                                   \
+            prt("[r%d]", a->rs);                                \
+            break;                                              \
+        case 3:                                                 \
+            prt("r%d", a->rs);                                  \
+            break;                                              \
+        }                                                       \
+        return true;                                            \
+    } while (0)
+
+/* bset rs, dsp[rd] */
+/* bset rs, rd */
+static bool trans_BSET_lr(DisasContext *ctx, arg_BSET_lr *a)
+{
+    BOP_RM(set);
+}
+
+/* bset #imm, rd */
+static bool trans_BSET_ri(DisasContext *ctx, arg_BSET_ri *a)
+{
+    prt("bset\t#%d,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* bclr #imm, dsp[rd] */
+static bool trans_BCLR_li(DisasContext *ctx, arg_BCLR_li *a)
+{
+BOP_IM(clr, a->rs);
+}
+
+/* bclr rs, dsp[rd] */
+/* bclr rs, rd */
+static bool trans_BCLR_lr(DisasContext *ctx, arg_BCLR_lr *a)
+{
+    BOP_RM(clr);
+}
+
+/* bclr #imm, rd */
+static bool trans_BCLR_ri(DisasContext *ctx, arg_BCLR_ri *a)
+{
+    prt("bclr\t#%d,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* btst #imm, dsp[rd] */
+static bool trans_BTST_li(DisasContext *ctx, arg_BTST_li *a)
+{
+    BOP_IM(tst, a->rs);
+}
+
+/* btst rs, dsp[rd] */
+/* btst rs, rd */
+static bool trans_BTST_lr(DisasContext *ctx, arg_BTST_lr *a)
+{
+    BOP_RM(tst);
+}
+
+/* btst #imm, rd */
+static bool trans_BTST_ri(DisasContext *ctx, arg_BTST_ri *a)
+{
+    prt("btst\t#%d,r%d", a->imm, a->rd);
+    return true;
+}
+
+/* bnot rs, dsp[rd] */
+/* bnot rs, rd */
+static bool trans_BNOT_lr(DisasContext *ctx, arg_BNOT_lr *a)
+{
+    BOP_RM(not);
+}
+
+/* bmcond #imm, dsp[rd] */
+/* bnot #imm, dsp[rd] */
+static bool trans_BMCnd_BNOT_mi(DisasContext *ctx, arg_BMCnd_BNOT_mi *a)
+{
+    if (a->cd == 15) {
+        BOP_IM(not, a->rd);
+    } else {
+        int dsp = rx_index_addr(a->ld, RX_MEMORY_BYTE, ctx);
+        prt("bm%s\t#%d,", cond[a->cd], a->imm);
+        if (dsp > 0) {
+            prt("%d", dsp);
+        }
+        prt("[%d]", a->rd);
+    }
+    return true;
+}
+
+/* bmcond #imm, rd */
+/* bnot #imm, rd */
+static bool trans_BMCnd_BNOT_ri(DisasContext *ctx, arg_BMCnd_BNOT_ri *a)
+{
+    if (a->cd == 15) {
+        prt("bnot\t#%d,r%d", a->imm, a->rd);
+    } else {
+        prt("bm%s\t#%d,r%d", cond[a->cd], a->imm, a->rd);
+    }
+    return true;
+}
+
+/* clrpsw psw */
+static bool trans_CLRPSW(DisasContext *ctx, arg_CLRPSW *a)
+{
+    prt("clrpsw\t%c", psw[a->cb]);
+    return true;
+}
+
+/* setpsw psw */
+static bool trans_SETPSW(DisasContext *ctx, arg_SETPSW *a)
+{
+    prt("setpsw\t%c", psw[a->cb]);
+    return true;
+}
+
+/* mvtipl #imm */
+static bool trans_MVTIPL(DisasContext *ctx, arg_MVTIPL *a)
+{
+    prt("movtipl\t#%d", a->imm);
+    return true;
+}
+
+/* mvtc #imm, rd */
+static bool trans_MVTC_i(DisasContext *ctx, arg_MVTC_i *a)
+{
+    prt("mvtc/t#0x%08x,%s", a->imm, cr[a->cr]);
+    return true;
+}
+
+/* mvtc rs, rd */
+static bool trans_MVTC_r(DisasContext *ctx, arg_MVTC_r *a)
+{
+    prt("mvtc/tr%d,%s", a->rs, cr[a->cr]);
+    return true;
+}
+
+/* mvfc rs, rd */
+static bool trans_MVFC(DisasContext *ctx, arg_MVFC *a)
+{
+    prt("mvfc/t%s,r%d", cr[a->cr], a->rd);
+    return true;
+}
+
+/* rtfi */
+static bool trans_RTFI(DisasContext *ctx, arg_RTFI *a)
+{
+    prt("rtfi");
+    return true;
+}
+
+/* rte */
+static bool trans_RTE(DisasContext *ctx, arg_RTE *a)
+{
+    prt("rte");
+    return true;
+}
+
+/* brk */
+static bool trans_BRK(DisasContext *ctx, arg_BRK *a)
+{
+    prt("brk");
+    return true;
+}
+
+/* int #imm */
+static bool trans_INT(DisasContext *ctx, arg_INT *a)
+{
+    prt("int\t#%d", a->imm);
+    return true;
+}
+
+/* wait */
+static bool trans_WAIT(DisasContext *ctx, arg_WAIT *a)
+{
+    prt("wait");
+    return true;
+}
+
+/* sccnd.[bwl] rd */
+/* sccnd.[bwl] dsp:[rd] */
+static bool trans_SCCnd(DisasContext *ctx, arg_SCCnd *a)
+{
+    int dsp;
+    prt("sc%s.%c\t", cond[a->cd], size[a->sz]);
+    if (a->ld < 3) {
+        dsp = rx_index_addr(a->sz, a->ld, ctx);
+        if (dsp > 0) {
+            prt("%d", dsp);
+        }
+        prt("[r%d]", a->rd);
+    } else {
+        prt("r%d", a->rd);
+    }
+    return true;
+}
+
+int print_insn_rx(bfd_vma addr, disassemble_info *dis)
+{
+    DisasContext ctx;
+    uint32_t insn;
+    int i;
+    ctx.dis = dis;
+    ctx.addr = addr;
+
+    insn = decode_load(&ctx);
+    if (!decode(&ctx, insn)) {
+        ctx.dis->fprintf_func(ctx.dis->stream, ".byte\t");
+        for (i = 0; i < ctx.addr - addr; i++) {
+            if (i > 0) {
+                ctx.dis->fprintf_func(ctx.dis->stream, ",");
+            }
+            ctx.dis->fprintf_func(ctx.dis->stream, "0x%02x", insn >> 24);
+            insn <<= 8;
+        }
+    }
+    return ctx.addr - addr;
+}
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 05/11] target/rx: miscellaneous functions
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (3 preceding siblings ...)
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 04/11] target/rx: RX disassembler Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 06/11] RX62N interrupt contorol uint Yoshinori Sato
                     ` (8 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 target/rx/Makefile.objs |  11 +++++
 target/rx/gdbstub.c     | 113 ++++++++++++++++++++++++++++++++++++++++++++++++
 target/rx/monitor.c     |  38 ++++++++++++++++
 3 files changed, 162 insertions(+)
 create mode 100644 target/rx/Makefile.objs
 create mode 100644 target/rx/gdbstub.c
 create mode 100644 target/rx/monitor.c

diff --git a/target/rx/Makefile.objs b/target/rx/Makefile.objs
new file mode 100644
index 0000000000..f63e1ca43f
--- /dev/null
+++ b/target/rx/Makefile.objs
@@ -0,0 +1,11 @@
+obj-y += translate.o op_helper.o helper.o cpu.o gdbstub.o disas.o
+obj-$(CONFIG_SOFTMMU) += monitor.o
+
+DECODETREE = $(SRC_PATH)/scripts/decodetree.py
+
+target/rx/decode.inc.c: \
+  $(SRC_PATH)/target/rx/insns.decode $(DECODETREE)
+	$(call quiet-command,\
+	  $(PYTHON) $(DECODETREE) --varinsnwidth 32 -o $@ $<, "GEN", $(TARGET_DIR)$@)
+
+target/rx/translate.o: target/rx/decode.inc.c
diff --git a/target/rx/gdbstub.c b/target/rx/gdbstub.c
new file mode 100644
index 0000000000..d1d484a612
--- /dev/null
+++ b/target/rx/gdbstub.c
@@ -0,0 +1,113 @@
+/*
+ * RX gdb server stub
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "exec/gdbstub.h"
+
+int rx_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    RXCPU *cpu = RXCPU(cs);
+    CPURXState *env = &cpu->env;
+
+    switch (n) {
+    case 0 ... 15:
+        return gdb_get_regl(mem_buf, env->regs[n]);
+    case 16:
+        return gdb_get_regl(mem_buf, (env->psw_u) ? env->regs[0] : env->usp);
+    case 17:
+        return gdb_get_regl(mem_buf, (!env->psw_u) ? env->regs[0] : env->isp);
+    case 18:
+        update_psw_o(env);
+        return gdb_get_regl(mem_buf, pack_psw(env));
+    case 19:
+        return gdb_get_regl(mem_buf, env->pc);
+    case 20:
+        return gdb_get_regl(mem_buf, env->intb);
+    case 21:
+        return gdb_get_regl(mem_buf, env->bpsw);
+    case 22:
+        return gdb_get_regl(mem_buf, env->bpc);
+    case 23:
+        return gdb_get_regl(mem_buf, env->fintv);
+    case 24:
+        return gdb_get_regl(mem_buf, env->fpsw);
+    case 25:
+        return 0;
+    }
+    return 0;
+}
+
+int rx_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    RXCPU *cpu = RXCPU(cs);
+    CPURXState *env = &cpu->env;
+
+    switch (n) {
+    case 0 ... 15:
+        env->regs[n] = ldl_p(mem_buf);
+        if (n == 0) {
+            if (env->psw_u) {
+                env->usp = env->regs[0];
+            } else {
+                env->isp = env->regs[0];
+            }
+        }
+        break;
+    case 16:
+        env->usp = ldl_p(mem_buf);
+        if (env->psw_u) {
+            env->regs[0] = ldl_p(mem_buf);
+        }
+        break;
+    case 17:
+        env->isp = ldl_p(mem_buf);
+        if (!env->psw_u) {
+            env->regs[0] = ldl_p(mem_buf);
+        }
+        break;
+    case 18:
+        env->psw = ldl_p(mem_buf);
+        rx_cpu_unpack_psw(env, 1);
+        break;
+    case 19:
+        env->pc = ldl_p(mem_buf);
+        break;
+    case 20:
+        env->intb = ldl_p(mem_buf);
+        break;
+    case 21:
+        env->bpsw = ldl_p(mem_buf);
+        break;
+    case 22:
+        env->bpc = ldl_p(mem_buf);
+        break;
+    case 23:
+        env->fintv = ldl_p(mem_buf);
+        break;
+    case 24:
+        env->fpsw = ldl_p(mem_buf);
+        break;
+    case 25:
+        return 8;
+    default:
+        return 0;
+    }
+
+    return 4;
+}
diff --git a/target/rx/monitor.c b/target/rx/monitor.c
new file mode 100644
index 0000000000..5d7a1e58b5
--- /dev/null
+++ b/target/rx/monitor.c
@@ -0,0 +1,38 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "monitor/monitor.h"
+#include "monitor/hmp-target.h"
+#include "hmp.h"
+
+void hmp_info_tlb(Monitor *mon, const QDict *qdict)
+{
+    CPUArchState *env = mon_get_cpu_env();
+
+    if (!env) {
+        monitor_printf(mon, "No CPU available\n");
+        return;
+    }
+}
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 06/11] RX62N interrupt contorol uint
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (4 preceding siblings ...)
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 05/11] target/rx: miscellaneous functions Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 07/11] RX62N internal timer modules Yoshinori Sato
                     ` (7 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

This implementation supported only ICUa.
Hardware manual.
https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf?key=086621e01bd70347c18ea7f794aa9cc3

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 hw/intc/Makefile.objs    |   1 +
 hw/intc/rx_icu.c         | 323 +++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/intc/rx_icu.h |  49 +++++++
 3 files changed, 373 insertions(+)
 create mode 100644 hw/intc/rx_icu.c
 create mode 100644 include/hw/intc/rx_icu.h

diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 301a8e972d..ff79edb54b 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -48,3 +48,4 @@ obj-$(CONFIG_ARM_GIC) += arm_gicv3_cpuif.o
 obj-$(CONFIG_MIPS_CPS) += mips_gic.o
 obj-$(CONFIG_NIOS2) += nios2_iic.o
 obj-$(CONFIG_OMPIC) += ompic.o
+obj-$(CONFIG_RX) += rx_icu.o
diff --git a/hw/intc/rx_icu.c b/hw/intc/rx_icu.c
new file mode 100644
index 0000000000..573cb144c6
--- /dev/null
+++ b/hw/intc/rx_icu.c
@@ -0,0 +1,323 @@
+/*
+ * RX Interrupt control unit
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/intc/rx_icu.h"
+#include "qemu/error-report.h"
+
+#define request(icu, n) (icu->ipr[icu->map[n]] << 8 | n)
+
+static qemu_irq *rxicu_pin(RXICUState *icu, int n_IRQ)
+{
+    if ((icu->fir & 0x8000) && (icu->fir & 0xff) == n_IRQ) {
+        return &icu->_fir;
+    } else {
+        return &icu->_irq;
+    }
+}
+
+static void rxicu_request(RXICUState *icu, int n_IRQ)
+{
+    int enable;
+
+    enable = icu->ier[n_IRQ / 8] & (1 << (n_IRQ & 7));
+    if (enable != 0 && icu->req_irq < 0) {
+        qemu_set_irq(*rxicu_pin(icu, n_IRQ), 0x1000 | request(icu, n_IRQ));
+        icu->req_irq = n_IRQ;
+    }
+}
+
+static void rxicu_set_irq(void *opaque, int n_IRQ, int level)
+{
+    RXICUState *icu = opaque;
+    struct IRQSource *src;
+    int issue;
+
+    if (n_IRQ >= 256) {
+        error_report("%s: IRQ %d out of range", __func__, n_IRQ);
+        return;
+    }
+
+    src = &icu->src[n_IRQ];
+
+    level = (level != 0);
+    switch (src->sense) {
+    case TRG_LEVEL:
+        /* level-sensitive irq */
+        issue = level;
+        src->level = level;
+        break;
+    case TRG_NEDGE:
+        issue = (level == 0 && src->level == 1);
+        src->level = level;
+        break;
+    case TRG_PEDGE:
+        issue = (level == 1 && src->level == 0);
+        src->level = level;
+        break;
+    case TRG_BEDGE:
+        issue = ((level ^ src->level) & 1);
+        src->level = level;
+        break;
+    }
+    if (issue == 0 && src->sense == TRG_LEVEL) {
+        icu->ir[n_IRQ] = 0;
+        if (icu->req_irq == n_IRQ) {
+            qemu_set_irq(*rxicu_pin(icu, n_IRQ), request(icu, n_IRQ));
+            icu->req_irq = -1;
+        }
+        return;
+    }
+    if (issue) {
+        rxicu_request(icu, n_IRQ);
+    }
+}
+
+static void rxicu_ack_irq(void *opaque, int no, int level)
+{
+    RXICUState *icu = opaque;
+    int i;
+    int n_IRQ;
+    int max_pri;
+
+    if (icu->req_irq < 0) {
+        return;
+    }
+    if (icu->src[icu->req_irq].sense != TRG_LEVEL) {
+        icu->ir[icu->req_irq] = 0;
+    }
+    icu->req_irq = -1;
+
+    max_pri = 0;
+    n_IRQ = -1;
+    for (i = 0; i < 256; i++) {
+        if (icu->ir[i]) {
+            if (max_pri < icu->ipr[icu->map[i]]) {
+                n_IRQ = i;
+                max_pri = icu->ipr[icu->map[i]];
+            }
+        }
+    }
+    if (n_IRQ >= 0) {
+        rxicu_request(icu, n_IRQ);
+    }
+}
+
+static uint64_t icu_read(void *opaque, hwaddr addr, unsigned size)
+{
+    hwaddr offset = addr & 0xfff;
+    RXICUState *icu = opaque;
+    int reg = addr & 0xff;
+    int error;
+
+    error = (!(offset == 0x2f0 && size == 2) &&
+             !(offset != 0x2f0 && size == 1));
+    if (!error) {
+        switch (offset) {
+        case 0x000 ... 0x0ff:
+            return icu->ir[reg] & 1;
+        case 0x100 ... 0x1ff:
+            return icu->dtcer[reg] & 1;
+        case 0x200 ... 0x21f:
+            return icu->ier[reg];
+        case 0x2e0:
+            return 0;
+        case 0x2f0:
+            return icu->fir & 0x80ff;
+        case 0x300 ... 0x38f:
+            return icu->ipr[reg] & 0x0f;
+        case 0x400:
+        case 0x404:
+        case 0x408:
+        case 0x40c:
+            return icu->dmasr[reg >> 2];
+        case 0x500 ... 0x51f:
+            return icu->src[64 + reg].sense << 2;
+        case 0x580:
+        case 0x582:
+            return 0;
+        case 0x581:
+            return icu->nmier;
+        case 0x583:
+            return icu->nmicr;
+        default:
+            error = 1;
+        }
+    }
+    if (error) {
+        error_report("rxicu: unsupported read request at %08lx", addr);
+    }
+    return 0xffffffffffffffffULL;
+}
+
+static void icu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    hwaddr offset = addr & 0xfff;
+    RXICUState *icu = opaque;
+    int reg = addr & 0xff;
+    int error;
+
+    error = (!(offset == 0x2f0 && size == 2) &&
+             !(offset != 0x2f0 && size == 1));
+
+    if (!error) {
+        switch (offset) {
+        case 0x000 ... 0x0ff:
+            if (icu->src[reg].sense != TRG_LEVEL && val == 0) {
+                icu->ir[reg] = 0;
+            }
+            break;
+        case 0x100 ... 0x1ff:
+            icu->dtcer[reg] = val & 1;
+            break;
+        case 0x200 ... 0x21f:
+            icu->ier[reg] = val;
+            break;
+        case 0x2e0:
+            if (val == 1) {
+                qemu_irq_pulse(icu->_swi);
+            }
+            break;
+        case 0x2f0:
+            icu->fir = val;
+            break;
+        case 0x300 ... 0x38f:
+            icu->ipr[reg] = val & 0x0f;
+            break;
+        case 0x400:
+        case 0x404:
+        case 0x408:
+        case 0x40c:
+            icu->dmasr[reg >> 2] = val;
+            break;
+        case 0x500 ... 0x50f:
+            icu->src[64 + reg].sense = val >> 2;
+            break;
+        case 0x582:
+            break;
+        case 0x581:
+            icu->nmier |= val & 7;
+            break;
+        case 0x583:
+            icu->nmicr = val;
+            break;
+        default:
+            error = 1;
+        }
+    }
+    if (error) {
+        error_report("rxicu: unsupported write request at %08lx", addr);
+    }
+}
+
+static const MemoryRegionOps icu_ops = {
+    .write = icu_write,
+    .read  = icu_read,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 2,
+    },
+};
+
+static void rxicu_realize(DeviceState *dev, Error **errp)
+{
+    RXICUState *icu = RXICU(dev);
+    int i, j;
+
+    for (i = j = 0; i < 256; i++) {
+        if (icu->init_sense[j] == i) {
+            icu->src[i].sense = TRG_LEVEL;
+            if (j < icu->nr_sense) {
+                j++;
+            }
+        } else
+            icu->src[i].sense = TRG_PEDGE;
+    }
+    icu->req_irq = -1;
+}
+
+static void rxicu_init(Object *obj)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    RXICUState *icu = RXICU(obj);
+
+    memory_region_init_io(&icu->memory, OBJECT(icu), &icu_ops,
+                          icu, "rx-icu", 0x600);
+    sysbus_init_mmio(d, &icu->memory);
+
+    qdev_init_gpio_in(DEVICE(d), rxicu_set_irq, 256);
+    qdev_init_gpio_in_named(DEVICE(d), rxicu_ack_irq, "ack", 1);
+    sysbus_init_irq(d, &icu->_irq);
+    sysbus_init_irq(d, &icu->_fir);
+    sysbus_init_irq(d, &icu->_swi);
+}
+
+static void rxicu_fini(Object *obj)
+{
+    RXICUState *icu = RXICU(obj);
+    g_free(icu->map);
+    g_free(icu->init_sense);
+}
+
+static const VMStateDescription vmstate_rxicu = {
+    .name = "rx-icu",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property rxicu_properties[] = {
+    DEFINE_PROP_STRING("icutype", RXICUState, icutype),
+    DEFINE_PROP_ARRAY("ipr-map", RXICUState, nr_irqs, map,
+                      qdev_prop_uint32, uint32_t),
+    DEFINE_PROP_ARRAY("trigger-level", RXICUState, nr_sense, init_sense,
+                      qdev_prop_uint32, uint32_t),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rxicu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = rxicu_realize;
+    dc->props = rxicu_properties;
+    dc->vmsd = &vmstate_rxicu;
+}
+
+static const TypeInfo rxicu_info = {
+    .name       = TYPE_RXICU,
+    .parent     = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RXICUState),
+    .instance_init = rxicu_init,
+    .instance_finalize = rxicu_fini,
+    .class_init = rxicu_class_init,
+};
+
+static void rxicu_register_types(void)
+{
+    type_register_static(&rxicu_info);
+}
+
+type_init(rxicu_register_types)
diff --git a/include/hw/intc/rx_icu.h b/include/hw/intc/rx_icu.h
new file mode 100644
index 0000000000..bc46b3079b
--- /dev/null
+++ b/include/hw/intc/rx_icu.h
@@ -0,0 +1,49 @@
+#ifndef RX_ICU_H
+#define RX_ICU_H
+
+#include "qemu-common.h"
+#include "hw/irq.h"
+
+struct IRQSource {
+    int sense;
+    int level;
+};
+
+struct RXICUState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion memory;
+    struct IRQSource src[256];
+    char *icutype;
+    uint32_t nr_irqs;
+    uint32_t *map;
+    uint32_t nr_sense;
+    uint32_t *init_sense;
+
+    uint8_t ir[256];
+    uint8_t dtcer[256];
+    uint8_t ier[32];
+    uint8_t ipr[142];
+    uint8_t dmasr[4];
+    uint16_t fir;
+    uint8_t nmisr;
+    uint8_t nmier;
+    uint8_t nmiclr;
+    uint8_t nmicr;
+    int req_irq;
+    qemu_irq _irq;
+    qemu_irq _fir;
+    qemu_irq _swi;
+};
+typedef struct RXICUState RXICUState;
+
+#define TYPE_RXICU "rxicu"
+#define RXICU(obj) OBJECT_CHECK(RXICUState, (obj), TYPE_RXICU)
+
+#define SWI 27
+#define TRG_LEVEL 0
+#define TRG_NEDGE 1
+#define TRG_PEDGE 2
+#define TRG_BEDGE 3
+
+#endif /* RX_ICU_H */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 07/11] RX62N internal timer modules
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (5 preceding siblings ...)
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 06/11] RX62N interrupt contorol uint Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 08/11] RX62N internal serial communication interface Yoshinori Sato
                     ` (6 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

renesas_tmr: 8bit timer modules.
renesas_cmt: 16bit compare match timer modules.
This part use many renesas's CPU.
Hardware manual.
https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf?key=086621e01bd70347c18ea7f794aa9cc3

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 hw/timer/Makefile.objs         |   2 +
 hw/timer/renesas_cmt.c         | 235 +++++++++++++++++++++++
 hw/timer/renesas_tmr.c         | 412 +++++++++++++++++++++++++++++++++++++++++
 include/hw/timer/renesas_cmt.h |  33 ++++
 include/hw/timer/renesas_tmr.h |  42 +++++
 5 files changed, 724 insertions(+)
 create mode 100644 hw/timer/renesas_cmt.c
 create mode 100644 hw/timer/renesas_tmr.c
 create mode 100644 include/hw/timer/renesas_cmt.h
 create mode 100644 include/hw/timer/renesas_tmr.h

diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 0e9a4530f8..e11aaf5bf5 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -40,6 +40,8 @@ obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
 
 obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
 
+obj-$(CONFIG_RX) += renesas_tmr.o renesas_cmt.o
+
 common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o
 common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o
 
diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c
new file mode 100644
index 0000000000..3d4a7d6ca6
--- /dev/null
+++ b/hw/timer/renesas_cmt.c
@@ -0,0 +1,235 @@
+/*
+ * Renesas 16bit Compare-match timer
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/timer/renesas_cmt.h"
+#include "qemu/error-report.h"
+
+#define freq_to_ns(freq) (1000000000LL / freq)
+static const int clkdiv[] = {8, 32, 128, 512};
+
+static void update_events(RCMTState *cmt, int ch)
+{
+    uint16_t diff;
+
+    if ((cmt->cmstr & (1 << ch)) != 0) {
+        diff = cmt->cmcor[ch] - cmt->cmcnt[ch];
+        timer_mod(cmt->timer[ch],
+              qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+              diff * freq_to_ns(cmt->input_freq) *
+              clkdiv[cmt->cmcr[ch] & 3]);
+    }
+}
+
+static uint64_t read_cmcnt(RCMTState *cmt, int ch)
+{
+    int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+    if (cmt->cmstr & (1 << ch)) {
+        delta = (now - cmt->tick[ch]) / freq_to_ns(cmt->input_freq);
+        delta /= clkdiv[cmt->cmcr[ch] & 0x03];
+        return cmt->cmcnt[ch] + delta;
+    } else {
+        return cmt->cmcnt[ch];
+    }
+}
+
+static uint64_t cmt_read(void *opaque, hwaddr addr, unsigned size)
+{
+    hwaddr offset = addr & 0x0f;
+    RCMTState *cmt = opaque;
+    int ch = offset / 0x08;
+    int error = 1;
+
+    if (offset == 0) {
+        return cmt->cmstr;
+        error = 0;
+    } else {
+        offset &= 0x07;
+        if (ch == 0) {
+            offset -= 0x02;
+        }
+        error = 0;
+        switch (offset) {
+        case 0:
+            return cmt->cmcr[ch];
+        case 2:
+            return read_cmcnt(cmt, ch);
+        case 4:
+            return cmt->cmcor[ch];
+        default:
+            error = 1;
+        }
+    }
+    if (error) {
+        error_report("rcmt: unsupported read request to %08lx", addr);
+    }
+    return 0xffffffffffffffffUL;
+}
+
+static void start_stop(RCMTState *cmt, int ch, int st)
+{
+    if (st) {
+        update_events(cmt, ch);
+    } else {
+        timer_del(cmt->timer[ch]);
+    }
+}
+
+static void cmt_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    hwaddr offset = addr & 0x0f;
+    RCMTState *cmt = opaque;
+    int ch = offset / 0x08;
+    int error = 1;
+
+    if (offset == 0) {
+        cmt->cmstr = val;
+        start_stop(cmt, 0, cmt->cmstr & 1);
+        start_stop(cmt, 1, (cmt->cmstr >> 1) & 1);
+        error = 0;
+    } else {
+        offset &= 0x07;
+        if (ch == 0) {
+            offset -= 0x02;
+        }
+        error = 0;
+        switch (offset) {
+        case 0:
+            cmt->cmcr[ch] = val;
+            break;
+        case 2:
+            cmt->cmcnt[ch] = val;
+            break;
+        case 4:
+            cmt->cmcor[ch] = val;
+            break;
+        default:
+            error = 1;
+        }
+        if (error == 0 && cmt->cmstr & (1 << ch)) {
+            update_events(cmt, ch);
+        }
+    }
+    if (error) {
+        error_report("rcmt: unsupported write request to %08lx", addr);
+    }
+}
+
+static const MemoryRegionOps cmt_ops = {
+    .write = cmt_write,
+    .read  = cmt_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 2,
+        .max_access_size = 2,
+    },
+};
+
+static void timer_events(RCMTState *cmt, int ch)
+{
+    cmt->cmcnt[ch] = 0;
+    cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    update_events(cmt, ch);
+    if (cmt->cmcr[ch] & 0x40) {
+        qemu_irq_pulse(cmt->cmi[ch]);
+    }
+}
+
+static void timer_event0(void *opaque)
+{
+    RCMTState *cmt = opaque;
+
+    timer_events(cmt, 0);
+}
+
+static void timer_event1(void *opaque)
+{
+    RCMTState *cmt = opaque;
+
+    timer_events(cmt, 1);
+}
+
+static void rcmt_reset(DeviceState *dev)
+{
+    RCMTState *cmt = RCMT(dev);
+    cmt->cmstr = 0;
+    cmt->cmcr[0] = cmt->cmcr[1] = 0;
+    cmt->cmcnt[0] = cmt->cmcnt[1] = 0;
+    cmt->cmcor[0] = cmt->cmcor[1] = 0xffff;
+}
+
+static void rcmt_init(Object *obj)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    RCMTState *cmt = RCMT(obj);
+    int i;
+
+    memory_region_init_io(&cmt->memory, OBJECT(cmt), &cmt_ops,
+                          cmt, "renesas-cmt", 0x10);
+    sysbus_init_mmio(d, &cmt->memory);
+
+    for (i = 0; i < 2; i++) {
+        sysbus_init_irq(d, &cmt->cmi[i]);
+    }
+    cmt->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, timer_event0, cmt);
+    cmt->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, timer_event1, cmt);
+}
+
+static const VMStateDescription vmstate_rcmt = {
+    .name = "rx-cmt",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property rcmt_properties[] = {
+    DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rcmt_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = rcmt_properties;
+    dc->vmsd = &vmstate_rcmt;
+    dc->reset = rcmt_reset;
+}
+
+static const TypeInfo rcmt_info = {
+    .name       = TYPE_RENESAS_CMT,
+    .parent     = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RCMTState),
+    .instance_init = rcmt_init,
+    .class_init = rcmt_class_init,
+};
+
+static void rcmt_register_types(void)
+{
+    type_register_static(&rcmt_info);
+}
+
+type_init(rcmt_register_types)
diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c
new file mode 100644
index 0000000000..f8d7c22fb4
--- /dev/null
+++ b/hw/timer/renesas_tmr.c
@@ -0,0 +1,412 @@
+/*
+ * Renesas 8bit timer
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/timer/renesas_tmr.h"
+#include "qemu/error-report.h"
+
+#define freq_to_ns(freq) (1000000000LL / freq)
+static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192};
+
+static void update_events(RTMRState *tmr, int ch)
+{
+    uint16_t diff[3];
+    uint16_t tcnt, tcora, tcorb;
+    int i, min, event;
+
+    if (tmr->tccr[ch] == 0) {
+        return ;
+    }
+    if ((tmr->tccr[ch] & 0x08) == 0) {
+        error_report("rtmr: unsupported count mode %02x", tmr->tccr[ch]);
+        return ;
+    }
+    if ((tmr->tccr[0] & 0x18) == 0x18) {
+        if (ch == 1) {
+            tmr->next[ch] = none;
+            return ;
+        }
+        tcnt = (tmr->tcnt[0] << 8) + tmr->tcnt[1];
+        tcora = (tmr->tcora[0] << 8) | tmr->tcora[1];
+        tcorb = (tmr->tcorb[0] << 8) | tmr->tcorb[1];
+        diff[0] = tcora - tcnt;
+        diff[1] = tcorb - tcnt;
+        diff[2] = 0x10000 - tcnt;
+    } else {
+        diff[0] = tmr->tcora[ch] - tmr->tcnt[ch];
+        diff[1] = tmr->tcorb[ch] - tmr->tcnt[ch];
+        diff[2] = 0x100 - tmr->tcnt[ch];
+    }
+    for (event = 0, min = diff[0], i = 1; i < 3; i++) {
+        if (min > diff[i]) {
+            event = i;
+            min = diff[i];
+        }
+    }
+    tmr->next[ch] = event + 1;
+    timer_mod(tmr->timer[ch],
+              diff[event] * freq_to_ns(tmr->input_freq) *
+              clkdiv[tmr->tccr[ch] & 7]);
+}
+
+#define UPDATE_TIME(tmr, ch, upd, delta)                                \
+    do {                                                                \
+        tmr->div_round[ch] += delta;                                    \
+        if (clkdiv[tmr->tccr[ch] & 0x07] > 0) {                         \
+            upd = tmr->div_round[ch] / clkdiv[tmr->tccr[ch] & 0x07];    \
+            tmr->div_round[ch] %= clkdiv[tmr->tccr[ch] & 0x07];         \
+        } else                                                          \
+            upd = 0;                                                    \
+    } while (0)
+
+static uint64_t read_tcnt(RTMRState *tmr, unsigned size, int ch)
+{
+    int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    int upd, ovf = 0;
+    uint16_t tcnt[2];
+    uint16_t cnt;
+
+    delta = (now - tmr->tick) / freq_to_ns(tmr->input_freq);
+    if (delta > 0) {
+        tmr->tick = now;
+
+        if ((tmr->tccr[1] & 0x18) == 0x08) {
+            UPDATE_TIME(tmr, 1, upd, delta);
+            if (upd >= 0x100) {
+                ovf = upd >> 8;
+                upd -= ovf;
+            }
+            tcnt[1] = tmr->tcnt[1] + upd;
+        }
+        switch (tmr->tccr[0] & 0x18) {
+        case 0x08:
+            UPDATE_TIME(tmr, 0, upd, delta);
+            tcnt[0] = tmr->tcnt[0] + upd;
+            break;
+        case 0x18:
+            if (ovf > 0) {
+                tcnt[0] = tmr->tcnt[0] + ovf;
+            }
+            break;
+        }
+    } else {
+        tcnt[0] = tmr->tcnt[0];
+        tcnt[1] = tmr->tcnt[1];
+    }
+    if (size == 1) {
+        return tcnt[ch];
+    } else {
+        cnt = (tmr->tcnt[0] << 8) | (tmr->tcnt[1] & 0xff);
+        return cnt;
+    }
+}
+
+static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size)
+{
+    hwaddr offset = addr & 0x1f;
+    RTMRState *tmr = opaque;
+    int ch = offset & 1;
+    int error = 0;
+
+    if (size == 1) {
+        switch (offset & 0x0e) {
+        case 0x00:
+            return tmr->tcr[ch] & 0xf8;
+        case 0x02:
+            return tmr->tcsr[ch] & 0xf8;
+        case 0x04:
+            return tmr->tcora[ch];
+        case 0x06:
+            return tmr->tcorb[ch];
+        case 0x08:
+            return read_tcnt(tmr, size, ch);
+        case 0x0a:
+            return tmr->tccr[ch];
+        default:
+            error = 1;
+        }
+    } else if (ch == 0) {
+        switch (offset & 0x0e) {
+        case 0x04:
+            return tmr->tcora[0] << 8 | tmr->tcora[1];
+        case 0x06:
+            return tmr->tcorb[0] << 8 | tmr->tcorb[1];;
+        case 0x08:
+            return read_tcnt(tmr, size, 0) & 0xff;
+        case 0x0a:
+            return tmr->tccr[0] << 8 | tmr->tccr[1];
+        default:
+            error = 1;
+        }
+    } else {
+        error = 1;
+    }
+    if (error) {
+        error_report("rtmr: unsupported read request to %08lx", addr);
+    }
+    return 0xffffffffffffffffULL;
+}
+
+static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    hwaddr offset = addr & 0x1f;
+    RTMRState *tmr = opaque;
+    int ch = offset & 1;
+    int error = 0;
+
+    if (size == 1) {
+        switch (offset & 0x0e) {
+        case 0x00:
+            tmr->tcr[ch] = val;
+            break;
+        case 0x02:
+            tmr->tcsr[ch] = val;
+            break;
+        case 0x04:
+            tmr->tcora[ch] = val;
+            update_events(tmr, ch);
+            break;
+        case 0x06:
+            tmr->tcora[ch] = val;
+            update_events(tmr, ch);
+            break;
+        case 0x08:
+            tmr->tcnt[ch] = val;
+            update_events(tmr, ch);
+            break;
+        case 0x0a:
+            tmr->tccr[ch] = val;
+            update_events(tmr, ch);
+            break;
+        default:
+            error = 1;
+        }
+    } else if (ch == 0) {
+        switch (offset & 0x0e) {
+        case 0x04:
+            tmr->tcora[0] = (val >> 8) & 0xff;
+            tmr->tcora[1] = val & 0xff;
+            update_events(tmr, 0);
+            update_events(tmr, 1);
+        case 0x06:
+            tmr->tcorb[0] = (val >> 8) & 0xff;
+            tmr->tcorb[1] = val & 0xff;
+            update_events(tmr, 0);
+            update_events(tmr, 1);
+            break;
+        case 0x08:
+            tmr->tcnt[0] = (val >> 8) & 0xff;
+            tmr->tcnt[1] = val & 0xff;
+            update_events(tmr, 0);
+            update_events(tmr, 1);
+            break;
+        case 0x0a:
+            tmr->tccr[0] = (val >> 8) & 0xff;
+            tmr->tccr[1] = val & 0xff;
+            update_events(tmr, 0);
+            update_events(tmr, 1);
+            break;
+        default:
+            error = 1;
+        }
+    } else {
+        error = 1;
+    }
+    if (error) {
+        error_report("rtmr: unsupported write request to %08lx", addr);
+    }
+}
+
+static const MemoryRegionOps tmr_ops = {
+    .write = tmr_write,
+    .read  = tmr_read,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 2,
+    },
+};
+
+static void timer_events(RTMRState *tmr, int ch)
+{
+    tmr->tcnt[ch] = read_tcnt(tmr, 1, ch);
+    if ((tmr->tccr[0] & 0x18) != 0x18) {
+        switch (tmr->next[ch]) {
+        case none:
+            break;
+        case cmia:
+            if (tmr->tcnt[ch] >= tmr->tcora[ch]) {
+                if ((tmr->tcr[ch] & 0x18) == 0x08) {
+                    tmr->tcnt[ch] = 0;
+                }
+                if ((tmr->tcr[ch] & 0x40)) {
+                    qemu_irq_pulse(tmr->cmia[ch]);
+                }
+                if (ch == 0 && (tmr->tccr[1] & 0x18) == 0x18) {
+                    tmr->tcnt[1]++;
+                    timer_events(tmr, 1);
+                }
+            }
+            break;
+        case cmib:
+            if (tmr->tcnt[ch] >= tmr->tcorb[ch]) {
+                if ((tmr->tcr[ch] & 0x18) == 0x10) {
+                    tmr->tcnt[ch] = 0;
+                }
+                if ((tmr->tcr[ch] & 0x80)) {
+                    qemu_irq_pulse(tmr->cmib[ch]);
+                }
+            }
+            break;
+        case ovi:
+            if ((tmr->tcnt[ch] >= 0x100) &&
+                (tmr->tcr[ch] & 0x20)) {
+                qemu_irq_pulse(tmr->ovi[ch]);
+            }
+            break;
+        }
+        tmr->tcnt[ch] &= 0xff;
+    } else {
+        uint32_t tcnt, tcora, tcorb;
+        if (ch == 1) {
+            return ;
+        }
+        tcnt = (tmr->tcnt[0] << 8) + tmr->tcnt[1];
+        tcora = (tmr->tcora[0] << 8) | tmr->tcora[1];
+        tcorb = (tmr->tcorb[0] << 8) | tmr->tcorb[1];
+        switch (tmr->next[ch]) {
+        case none:
+            break;
+        case cmia:
+            if (tcnt >= tcora) {
+                if ((tmr->tcr[ch] & 0x18) == 0x08) {
+                    tcnt = 0;
+                }
+                if ((tmr->tcr[ch] & 0x40)) {
+                    qemu_irq_pulse(tmr->cmia[ch]);
+                }
+            }
+            break;
+        case cmib:
+            if (tcnt >= tcorb) {
+                if ((tmr->tcr[ch] & 0x18) == 0x10) {
+                    tcnt = 0;
+                }
+                if ((tmr->tcr[ch] & 0x80)) {
+                    qemu_irq_pulse(tmr->cmib[ch]);
+                }
+            }
+            break;
+        case ovi:
+            if ((tcnt >= 0x10000) &&
+                (tmr->tcr[ch] & 0x20)) {
+                qemu_irq_pulse(tmr->ovi[ch]);
+            }
+            break;
+        }
+        tmr->tcnt[0] = (tcnt >> 8) & 0xff;
+        tmr->tcnt[1] = tcnt & 0xff;
+    }
+    update_events(tmr, ch);
+}
+
+static void timer_event0(void *opaque)
+{
+    RTMRState *tmr = opaque;
+
+    timer_events(tmr, 0);
+}
+
+static void timer_event1(void *opaque)
+{
+    RTMRState *tmr = opaque;
+
+    timer_events(tmr, 1);
+}
+
+static void rtmr_reset(DeviceState *dev)
+{
+    RTMRState *tmr = RTMR(dev);
+    tmr->tcora[0] = tmr->tcora[1] = 0xff;
+    tmr->tcorb[0] = tmr->tcorb[1] = 0xff;
+    tmr->tcsr[0] = 0x00;
+    tmr->tcsr[1] = 0x10;
+    tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+static void rtmr_init(Object *obj)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    RTMRState *tmr = RTMR(obj);
+    int i;
+
+    memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
+                          tmr, "rx-tmr", 0x10);
+    sysbus_init_mmio(d, &tmr->memory);
+
+    for (i = 0; i < 2; i++) {
+        sysbus_init_irq(d, &tmr->cmia[i]);
+        sysbus_init_irq(d, &tmr->cmib[i]);
+        sysbus_init_irq(d, &tmr->ovi[i]);
+    }
+    tmr->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, timer_event0, tmr);
+    tmr->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, timer_event1, tmr);
+}
+
+static const VMStateDescription vmstate_rtmr = {
+    .name = "rx-cmt",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property rtmr_properties[] = {
+    DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rtmr_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = rtmr_properties;
+    dc->vmsd = &vmstate_rtmr;
+    dc->reset = rtmr_reset;
+}
+
+static const TypeInfo rtmr_info = {
+    .name       = TYPE_RENESAS_TMR,
+    .parent     = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RTMRState),
+    .instance_init = rtmr_init,
+    .class_init = rtmr_class_init,
+};
+
+static void rtmr_register_types(void)
+{
+    type_register_static(&rtmr_info);
+}
+
+type_init(rtmr_register_types)
diff --git a/include/hw/timer/renesas_cmt.h b/include/hw/timer/renesas_cmt.h
new file mode 100644
index 0000000000..764759d4ad
--- /dev/null
+++ b/include/hw/timer/renesas_cmt.h
@@ -0,0 +1,33 @@
+/*
+ * Renesas Compare-match timer Object
+ *
+ * Copyright (c) 2018 Yoshinori Sato
+ *
+ * This code is licensed under the GPL version 2 or later.
+ *
+ */
+
+#ifndef HW_RENESAS_CMT_H
+#define HW_RENESAS_CMT_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_RENESAS_CMT "renesas-cmt"
+#define RCMT(obj) OBJECT_CHECK(RCMTState, (obj), TYPE_RENESAS_CMT)
+
+typedef struct RCMTState {
+    SysBusDevice parent_obj;
+
+    uint64_t input_freq;
+    MemoryRegion memory;
+
+    uint16_t cmstr;
+    uint16_t cmcr[2];
+    uint16_t cmcnt[2];
+    uint16_t cmcor[2];
+    int64_t tick[2];
+    qemu_irq cmi[2];
+    QEMUTimer *timer[2];
+} RCMTState;
+
+#endif
diff --git a/include/hw/timer/renesas_tmr.h b/include/hw/timer/renesas_tmr.h
new file mode 100644
index 0000000000..09333c86fc
--- /dev/null
+++ b/include/hw/timer/renesas_tmr.h
@@ -0,0 +1,42 @@
+/*
+ * Renesas 8bit timer Object
+ *
+ * Copyright (c) 2018 Yoshinori Sato
+ *
+ * This code is licensed under the GPL version 2 or later.
+ *
+ */
+
+#ifndef HW_RENESAS_TMR_H
+#define HW_RENESAS_TMR_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_RENESAS_TMR "renesas-tmr"
+#define RTMR(obj) OBJECT_CHECK(RTMRState, (obj), TYPE_RENESAS_TMR)
+
+enum timer_event {none, cmia, cmib, ovi};
+
+typedef struct RTMRState {
+    SysBusDevice parent_obj;
+
+    uint64_t input_freq;
+    MemoryRegion memory;
+
+    uint16_t tcnt[2];
+    uint8_t tcora[2];
+    uint8_t tcorb[2];
+    uint8_t tcr[2];
+    uint8_t tccr[2];
+    uint8_t tcor[2];
+    uint8_t tcsr[2];
+    int64_t tick;
+    int64_t div_round[2];
+    enum timer_event next[2];
+    qemu_irq cmia[2];
+    qemu_irq cmib[2];
+    qemu_irq ovi[2];
+    QEMUTimer *timer[2];
+} RTMRState;
+
+#endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 08/11] RX62N internal serial communication interface
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (6 preceding siblings ...)
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 07/11] RX62N internal timer modules Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02 19:21     ` Philippe Mathieu-Daudé
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 09/11] RX Target hardware definition Yoshinori Sato
                     ` (5 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

This module supported only non FIFO type.
Hardware manual.
https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf?key=086621e01bd70347c18ea7f794aa9cc3

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 hw/char/Makefile.objs         |   2 +-
 hw/char/renesas_sci.c         | 288 ++++++++++++++++++++++++++++++++++++++++++
 include/hw/char/renesas_sci.h |  42 ++++++
 3 files changed, 331 insertions(+), 1 deletion(-)
 create mode 100644 hw/char/renesas_sci.c
 create mode 100644 include/hw/char/renesas_sci.h

diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
index c4947d7ae7..68eae7b9a5 100644
--- a/hw/char/Makefile.objs
+++ b/hw/char/Makefile.objs
@@ -15,7 +15,7 @@ common-obj-$(CONFIG_CADENCE) += cadence_uart.o
 obj-$(CONFIG_EXYNOS4) += exynos4210_uart.o
 obj-$(CONFIG_COLDFIRE) += mcf_uart.o
 obj-$(CONFIG_OMAP) += omap_uart.o
-obj-$(CONFIG_SH4) += sh_serial.o
+obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o
 obj-$(CONFIG_PSERIES) += spapr_vty.o
 obj-$(CONFIG_DIGIC) += digic-uart.o
 obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c
new file mode 100644
index 0000000000..56d070a329
--- /dev/null
+++ b/hw/char/renesas_sci.c
@@ -0,0 +1,288 @@
+/*
+ * Renesas Serial Communication Interface
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/char/renesas_sci.h"
+#include "qemu/error-report.h"
+
+#define freq_to_ns(freq) (1000000000LL / freq)
+
+static int can_receive(void *opaque)
+{
+    RSCIState *sci = RSCI(opaque);
+    if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
+        return 0;
+    } else {
+        return sci->scr & 0x10;
+    }
+}
+
+static void receive(void *opaque, const uint8_t *buf, int size)
+{
+    RSCIState *sci = RSCI(opaque);
+    sci->rdr = buf[0];
+    if (sci->ssr & 0x40 || size > 1) {
+        sci->ssr |= 0x20;
+        if (sci->scr & 0x40) {
+            qemu_set_irq(sci->irq[ERI], 1);
+        }
+    } else {
+        sci->ssr |= 0x40;
+        sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime;
+        if (sci->scr & 0x40) {
+            qemu_set_irq(sci->irq[RXI], 1);
+            qemu_set_irq(sci->irq[RXI], 0);
+        }
+    }
+}
+
+static void send_byte(RSCIState *sci)
+{
+    if (qemu_chr_fe_backend_connected(&sci->chr)) {
+        qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1);
+    }
+    timer_mod(sci->timer,
+              qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime);
+    sci->ssr &= ~0x04;
+    sci->ssr |= 0x80;
+    qemu_set_irq(sci->irq[TEI], 0);
+    if (sci->scr & 0x80) {
+        qemu_set_irq(sci->irq[TXI], 1);
+        qemu_set_irq(sci->irq[TXI], 0);
+    }
+}
+
+static void txend(void *opaque)
+{
+    RSCIState *sci = RSCI(opaque);
+    if ((sci->ssr & 0x80) == 0) {
+        send_byte(sci);
+    } else {
+        sci->ssr |= 0x04;
+        if (sci->scr & 0x04) {
+            qemu_set_irq(sci->irq[TEI], 1);
+        }
+    }
+}
+
+static void update_trtime(RSCIState *sci)
+{
+    static const int div[] = {1, 4, 16, 64};
+    int w;
+
+    w = (sci->smr & 0x40) ? 7 : 8;      /* CHR */
+    w += (sci->smr >> 5) & 1;           /* PE */
+    w += (sci->smr & 0x08) ? 2 : 1;     /* STOP */
+    sci->trtime = w * freq_to_ns(sci->input_freq) *
+        32 * div[sci->smr & 0x03] * sci->brr;
+}
+
+static void sci_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    hwaddr offset = addr & 0x07;
+    RSCIState *sci = RSCI(opaque);
+    int error = 0;
+
+    switch (offset) {
+    case 0: /* SMR */
+        if ((sci->scr & 0x30) == 0) {
+            sci->smr = val;
+            update_trtime(sci);
+        }
+        break;
+    case 1: /* BRR */
+        if ((sci->scr & 0x30) == 0) {
+            sci->brr = val;
+            update_trtime(sci);
+        }
+        break;
+    case 2: /* SCR */
+        sci->scr = val;
+        if (sci->scr & 0x20) {
+            sci->ssr |= 0x84;
+            qemu_set_irq(sci->irq[TXI], 1);
+            qemu_set_irq(sci->irq[TXI], 0);
+        }
+        if ((sci->scr & 0x04) == 0) {
+            qemu_set_irq(sci->irq[TEI], 0);
+        }
+        if ((sci->scr & 0x40) == 0) {
+            qemu_set_irq(sci->irq[ERI], 0);
+        }
+        break;
+    case 3: /* TDR */
+        sci->tdr = val;
+        if (sci->ssr & 0x04) {
+            send_byte(sci);
+        } else{
+            sci->ssr &= ~0x80;
+        }
+        break;
+    case 4: /* SSR */
+        sci->ssr &= ~0x38 | (val & 0x38);
+        if (((sci->read_ssr & 0x38) ^ (sci->ssr & 0x38)) &&
+            (sci->ssr & 0x38) == 0) {
+            qemu_set_irq(sci->irq[ERI], 0);
+        }
+        break;
+    case 5: /* RDR */
+        error = 1; break;
+    case 6: /* SCMR */
+        sci->scmr = val; break;
+    case 7: /* SEMR */
+        sci->semr = val; break;
+    }
+
+    if (error) {
+        error_report("rsci: unsupported write request to %08lx", addr);
+    }
+}
+
+static uint64_t sci_read(void *opaque, hwaddr addr, unsigned size)
+{
+    hwaddr offset = addr & 0x07;
+    RSCIState *sci = RSCI(opaque);
+    int error = 0;
+    switch (offset) {
+    case 0: /* SMR */
+        return sci->smr;
+    case 1: /* BRR */
+        return sci->brr;
+    case 2: /* SCR */
+        return sci->scr;
+    case 3: /* TDR */
+        return sci->tdr;
+    case 4: /* SSR */
+        sci->read_ssr = sci->ssr;
+        return sci->ssr;
+    case 5: /* RDR */
+        sci->ssr &= ~0x40;
+        return sci->rdr;
+    case 6: /* SCMR */
+        return sci->scmr;
+    case 7: /* SEMR */
+        return sci->semr;
+    }
+
+    if (error) {
+        error_report("rsci: unsupported write request to %08lx", addr);
+    }
+    return -1;
+}
+
+static const MemoryRegionOps sci_ops = {
+    .write = sci_write,
+    .read  = sci_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static void rsci_reset(DeviceState *dev)
+{
+    RSCIState *sci = RSCI(dev);
+    sci->smr = sci->scr = 0x00;
+    sci->brr = 0xff;
+    sci->tdr = 0xff;
+    sci->rdr = 0x00;
+    sci->ssr = 0x84;
+    sci->scmr = 0x00;
+    sci->semr = 0x00;
+    sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+static void sci_event(void *opaque, int event)
+{
+    RSCIState *sci = RSCI(opaque);
+    if (event == CHR_EVENT_BREAK) {
+        sci->ssr |= 0x10;
+        if (sci->scr & 0x40) {
+            qemu_set_irq(sci->irq[ERI], 1);
+        }
+    }
+}
+
+static void rsci_realize(DeviceState *dev, Error **errp)
+{
+    RSCIState *sci = RSCI(dev);
+
+    qemu_chr_fe_set_handlers(&sci->chr, can_receive, receive,
+                             sci_event, NULL, sci, NULL, true);
+}
+
+static void rsci_init(Object *obj)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    RSCIState *sci = RSCI(obj);
+    int i;
+
+    memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops,
+                          sci, "renesas-sci", 0x8);
+    sysbus_init_mmio(d, &sci->memory);
+
+    for (i = 0; i < 4; i++) {
+        sysbus_init_irq(d, &sci->irq[i]);
+    }
+    sci->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, txend, sci);
+}
+
+static const VMStateDescription vmstate_rcmt = {
+    .name = "renesas-sci",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property rsci_properties[] = {
+    DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0),
+    DEFINE_PROP_CHR("chardev", RSCIState, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rsci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = rsci_realize;
+    dc->props = rsci_properties;
+    dc->vmsd = &vmstate_rcmt;
+    dc->reset = rsci_reset;
+}
+
+static const TypeInfo rsci_info = {
+    .name       = TYPE_RENESAS_SCI,
+    .parent     = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RSCIState),
+    .instance_init = rsci_init,
+    .class_init = rsci_class_init,
+};
+
+static void rsci_register_types(void)
+{
+    type_register_static(&rsci_info);
+}
+
+type_init(rsci_register_types)
diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h
new file mode 100644
index 0000000000..47e3e7a5d7
--- /dev/null
+++ b/include/hw/char/renesas_sci.h
@@ -0,0 +1,42 @@
+/*
+ * Renesas Serial Communication Interface
+ *
+ * Copyright (c) 2018 Yoshinori Sato
+ *
+ * This code is licensed under the GPL version 2 or later.
+ *
+ */
+
+#include "chardev/char-fe.h"
+#include "qemu/timer.h"
+#include "hw/sysbus.h"
+
+#define TYPE_RENESAS_SCI "renesas-sci"
+#define RSCI(obj) OBJECT_CHECK(RSCIState, (obj), TYPE_RENESAS_SCI)
+
+#define ERI 0
+#define RXI 1
+#define TXI 2
+#define TEI 3
+
+typedef struct {
+    SysBusDevice parent_obj;
+    MemoryRegion memory;
+
+    uint8_t smr;
+    uint8_t brr;
+    uint8_t scr;
+    uint8_t tdr;
+    uint8_t ssr;
+    uint8_t rdr;
+    uint8_t scmr;
+    uint8_t semr;
+
+    uint8_t read_ssr;
+    long long trtime;
+    long long rx_next;
+    QEMUTimer *timer;
+    CharBackend chr;
+    uint64_t input_freq;
+    qemu_irq irq[4];
+} RSCIState;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 09/11] RX Target hardware definition
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (7 preceding siblings ...)
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 08/11] RX62N internal serial communication interface Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 10/11] Add rx-softmmu Yoshinori Sato
                     ` (4 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

rx62n -  RX62N cpu.
rxqemu - QEMU virtual target.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 hw/rx/Makefile.objs   |   1 +
 hw/rx/rx62n.c         | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/rx/rxqemu.c        | 100 ++++++++++++++++++++++
 include/hw/rx/rx.h    |   7 ++
 include/hw/rx/rx62n.h |  54 ++++++++++++
 5 files changed, 389 insertions(+)
 create mode 100644 hw/rx/Makefile.objs
 create mode 100644 hw/rx/rx62n.c
 create mode 100644 hw/rx/rxqemu.c
 create mode 100644 include/hw/rx/rx.h
 create mode 100644 include/hw/rx/rx62n.h

diff --git a/hw/rx/Makefile.objs b/hw/rx/Makefile.objs
new file mode 100644
index 0000000000..e2edbb527e
--- /dev/null
+++ b/hw/rx/Makefile.objs
@@ -0,0 +1 @@
+obj-y += rx62n.o rxqemu.o
diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
new file mode 100644
index 0000000000..bab1a4ad8f
--- /dev/null
+++ b/hw/rx/rx62n.c
@@ -0,0 +1,227 @@
+/*
+ * RX62N device
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/rx/rx62n.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "exec/address-spaces.h"
+
+static const int ipr_table[] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 15 */
+    0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0x02,
+    0xff, 0xff, 0xff, 0x03, 0x04, 0x05, 0x06, 0x07, /* 31 */
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x14, 0x14, 0x14, /* 47 */
+    0x15, 0x15, 0x15, 0x15, 0xff, 0xff, 0xff, 0xff,
+    0x18, 0x18, 0x18, 0x18, 0x18, 0x1d, 0x1e, 0x1f, /* 63 */
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 79 */
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0x3a, 0x3b, 0x3c, 0xff, 0xff, 0xff, /* 95 */
+    0x40, 0xff, 0x44, 0x45, 0xff, 0xff, 0x48, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 111 */
+    0xff, 0xff, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52,
+    0x52, 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, /* 127 */
+    0x56, 0x57, 0x57, 0x57, 0x57, 0x58, 0x59, 0x59,
+    0x59, 0x59, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, /* 143 */
+    0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f,
+    0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x62, /* 159 */
+    0x62, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x66,
+    0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, /* 175 */
+    0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b,
+    0x6b, 0x6b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 191 */
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x71,
+    0x72, 0x73, 0x74, 0x75, 0xff, 0xff, 0xff, 0xff, /* 207 */
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x80,
+    0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, /* 223 */
+    0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0xff, 0xff,
+    0xff, 0xff, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, /* 239 */
+    0x86, 0x86, 0xff, 0xff, 0xff, 0xff, 0x88, 0x89,
+    0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, /* 255 */
+};
+
+static const uint32_t levelirq[] = {
+     16,  21,  32,  44,  47,  48,  51,  64,  65,  66,
+     67,  68,  69,  70,  71,  72,  73,  74,  75,  76,
+     77,  78,  79,  90,  91, 170, 171, 172, 173, 214,
+    217, 218, 221, 222, 225, 226, 229, 234, 237, 238,
+    241, 246, 249, 250, 253,
+};
+
+static RXICUState *register_icu(RX62NState *s)
+{
+    SysBusDevice *icu;
+    int i;
+
+    icu = SYS_BUS_DEVICE(qdev_create(NULL, TYPE_RXICU));
+    sysbus_mmio_map(icu, 0, 0x00087000);
+    qdev_prop_set_string(DEVICE(icu), "icutype", "icua");
+    qdev_prop_set_uint32(DEVICE(icu), "len-ipr-map", 256);
+    for (i = 0; i < 256; i++) {
+        char propname[32];
+        snprintf(propname, sizeof(propname), "ipr-map[%d]", i);
+        qdev_prop_set_uint32(DEVICE(icu), propname, ipr_table[i]);
+    }
+    qdev_prop_set_uint32(DEVICE(icu), "len-trigger-level", 256);
+    for (i = 0; i < ARRAY_SIZE(levelirq); i++) {
+        char propname[32];
+        snprintf(propname, sizeof(propname), "trigger-level[%d]", i);
+        qdev_prop_set_uint32(DEVICE(icu), propname, levelirq[i]);
+    }
+    for (i = 0; i < 256; i++) {
+        s->irq[i] = qdev_get_gpio_in(DEVICE(icu), i);
+    }
+
+    qdev_init_nofail(DEVICE(icu));
+    sysbus_connect_irq(SYS_BUS_DEVICE(icu), 0,
+                       qdev_get_gpio_in(DEVICE(s->cpu), RX_CPU_IRQ));
+    sysbus_connect_irq(SYS_BUS_DEVICE(icu), 1,
+                       qdev_get_gpio_in(DEVICE(s->cpu), RX_CPU_FIR));
+    sysbus_connect_irq(SYS_BUS_DEVICE(icu), 2, s->irq[SWI]);
+
+    return RXICU(icu);
+}
+
+static RTMRState *register_tmr(RX62NState *s, int unit)
+{
+    SysBusDevice *tmr;
+    int i, irqbase;
+
+    tmr = SYS_BUS_DEVICE(qdev_create(NULL, TYPE_RENESAS_TMR));
+    sysbus_mmio_map(tmr, 0, 0x00088200 + unit * 0x10);
+    qdev_prop_set_uint64(DEVICE(tmr), "input-freq", 48000000);
+
+    qdev_init_nofail(DEVICE(tmr));
+    irqbase = 174 + 6 * unit;
+    for (i = 0; i < 6; i++) {
+        sysbus_connect_irq(tmr, i, s->irq[irqbase + i]);
+    }
+
+    return RTMR(tmr);
+}
+
+static RCMTState *register_cmt(RX62NState *s, int unit)
+{
+    SysBusDevice *cmt;
+    int i, irqbase;
+
+    cmt = SYS_BUS_DEVICE(qdev_create(NULL, TYPE_RENESAS_CMT));
+    sysbus_mmio_map(cmt, 0, 0x00088000 + unit * 0x10);
+    qdev_prop_set_uint64(DEVICE(cmt), "input-freq", 48000000);
+
+    qdev_init_nofail(DEVICE(cmt));
+    irqbase = 28 + 2 * unit;
+    for (i = 0; i < 1; i++) {
+        sysbus_connect_irq(cmt, i, s->irq[irqbase + i]);
+    }
+
+    return RCMT(cmt);
+}
+
+static RSCIState *register_sci(RX62NState *s, int unit)
+{
+    SysBusDevice *sci;
+    int i, irqbase;
+
+    sci = SYS_BUS_DEVICE(qdev_create(NULL, TYPE_RENESAS_SCI));
+    qdev_prop_set_chr(DEVICE(sci), "chardev", serial_hd(unit));
+    qdev_prop_set_uint64(DEVICE(sci), "input-freq", 48000000);
+    qdev_init_nofail(DEVICE(sci));
+    sysbus_mmio_map(sci, 0, 0x00088240 + unit * 0x08);
+    irqbase = 214 + 4 * unit;
+    for (i = 0; i < 4; i++) {
+        sysbus_connect_irq(sci, i, s->irq[irqbase + i]);
+    }
+
+    object_property_set_bool(OBJECT(sci), true, "realized", NULL);
+    return RSCI(sci);
+}
+
+static void rx62n_realize(DeviceState *dev, Error **errp)
+{
+    RX62NState *s = RX62N(dev);
+    Error *err = NULL;
+
+    memory_region_init_ram(&s->iram, NULL, "iram", 0x18000, NULL);
+    memory_region_add_subregion(s->sysmem, 0x00000000, &s->iram);
+    memory_region_init_rom(&s->d_flash, NULL, "dataflash", 0x8000, NULL);
+    memory_region_add_subregion(s->sysmem, 0x00100000, &s->d_flash);
+    memory_region_init_rom(&s->c_flash, NULL, "codeflash", 0x80000, NULL);
+    memory_region_add_subregion(s->sysmem, 0xfff80000, &s->c_flash);
+
+    s->cpu = RXCPU(object_new(TYPE_RXCPU));
+
+    if (!s->kernel) {
+        rom_add_file_fixed(bios_name, 0xfff80000, 0);
+    }
+
+    object_property_set_bool(OBJECT(s->cpu), true, "realized", &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    s->icu = register_icu(s);
+    s->cpu->env.ack = qdev_get_gpio_in_named(DEVICE(s->icu), "ack", 0);
+    s->tmr[0] = register_tmr(s, 0);
+    s->tmr[1] = register_tmr(s, 1);
+    s->cmt[0] = register_cmt(s, 0);
+    s->cmt[1] = register_cmt(s, 1);
+    s->sci[0] = register_sci(s, 0);
+}
+
+static void rx62n_init(Object *obj)
+{
+}
+
+static Property rx62n_properties[] = {
+    DEFINE_PROP_LINK("memory", RX62NState, sysmem, TYPE_MEMORY_REGION,
+                     MemoryRegion *),
+    DEFINE_PROP_BOOL("load-kernel", RX62NState, kernel, false),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rx62n_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = rx62n_realize;
+    dc->props = rx62n_properties;
+}
+
+static const TypeInfo rx62n_info = {
+    .name = TYPE_RX62N,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RX62NState),
+    .instance_init = rx62n_init,
+    .class_init = rx62n_class_init,
+};
+
+static void rx62n_register_types(void)
+{
+    type_register_static(&rx62n_info);
+}
+
+type_init(rx62n_register_types)
diff --git a/hw/rx/rxqemu.c b/hw/rx/rxqemu.c
new file mode 100644
index 0000000000..87cd30b2f1
--- /dev/null
+++ b/hw/rx/rxqemu.c
@@ -0,0 +1,100 @@
+/*
+ * RX QEMU virtual target
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/loader.h"
+#include "hw/rx/rx62n.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "sysemu/device_tree.h"
+#include "hw/boards.h"
+#include "exec/address-spaces.h"
+#include "exec/cpu-all.h"
+
+static void rxqemu_init(MachineState *machine)
+{
+    DeviceState *cpu;
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *sdram = g_new(MemoryRegion, 1);
+    const char *kernel_filename = machine->kernel_filename;
+    const char *dtb_filename = machine->dtb;
+    void *dtb = NULL;
+    int dtb_size;
+
+    /* Allocate memory space */
+    memory_region_init_ram(sdram, NULL, "rxqemu.sdram", 0x01000000,
+                           &error_fatal);
+    memory_region_add_subregion(sysmem, 0x01000000, sdram);
+
+    cpu = qdev_create(NULL, TYPE_RX62N);
+    object_property_set_link(OBJECT(cpu), OBJECT(get_system_memory()),
+                             "memory", &error_abort);
+    object_property_set_bool(OBJECT(cpu), kernel_filename != NULL,
+                             "load-kernel", &error_abort);
+    /* This will exit with an error if the user passed us a bad cpu_type */
+    qdev_init_nofail(cpu);
+
+    if (kernel_filename) {
+        rx_load_image(RXCPU(first_cpu), kernel_filename,
+                      0x01800000, 0x00800000);
+    }
+    if (dtb_filename) {
+        dtb = load_device_tree(dtb_filename, &dtb_size);
+        if (dtb == NULL) {
+            fprintf(stderr, "Couldn't open dtb file %s\n", dtb_filename);
+            exit(1);
+        }
+        if (machine->kernel_cmdline &&
+            qemu_fdt_setprop_string(dtb, "/chosen", "bootargs",
+                                    machine->kernel_cmdline) < 0) {
+            fprintf(stderr, "couldn't set /chosen/bootargs\n");
+            exit(1);
+        }
+        rom_add_blob_fixed("dtb", dtb, dtb_size, 0x02000000 - dtb_size);
+        /* Set dtb address to R1 */
+        RXCPU(first_cpu)->env.regs[1] = 0x02000000 - dtb_size;
+    }
+}
+
+static void rxqemu_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->desc = "RX QEMU Virtual Target";
+    mc->init = rxqemu_init;
+    mc->is_default = 1;
+    mc->default_cpu_type = TYPE_RXCPU;
+}
+
+static const TypeInfo rxqemu_type = {
+    .name = MACHINE_TYPE_NAME("rx-qemu"),
+    .parent = TYPE_MACHINE,
+    .class_init = rxqemu_class_init,
+};
+
+static void rxqemu_machine_init(void)
+{
+    type_register_static(&rxqemu_type);
+}
+
+type_init(rxqemu_machine_init)
diff --git a/include/hw/rx/rx.h b/include/hw/rx/rx.h
new file mode 100644
index 0000000000..ff5924b81f
--- /dev/null
+++ b/include/hw/rx/rx.h
@@ -0,0 +1,7 @@
+#ifndef QEMU_RX_H
+#define QEMU_RX_H
+/* Definitions for RX board emulation.  */
+
+#include "target/rx/cpu-qom.h"
+
+#endif
diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
new file mode 100644
index 0000000000..8c15399ce0
--- /dev/null
+++ b/include/hw/rx/rx62n.h
@@ -0,0 +1,54 @@
+/*
+ * RX62N Object
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_RX_RX62N_H
+#define HW_RX_RX62N_H
+
+#include "hw/sysbus.h"
+#include "hw/rx/rx.h"
+#include "hw/intc/rx_icu.h"
+#include "hw/timer/renesas_tmr.h"
+#include "hw/timer/renesas_cmt.h"
+#include "hw/char/renesas_sci.h"
+
+#define TYPE_RX62N "rx62n"
+#define TYPE_RX62N_CPU RX_CPU_TYPE_NAME(TYPE_RX62N)
+#define RX62N(obj) OBJECT_CHECK(RX62NState, (obj), TYPE_RX62N)
+
+typedef struct RX62NState {
+    SysBusDevice parent_obj;
+
+    RXCPU *cpu;
+    RXICUState *icu;
+    RTMRState *tmr[2];
+    RCMTState *cmt[2];
+    RSCIState *sci[6];
+
+    MemoryRegion *sysmem;
+    bool kernel;
+
+    MemoryRegion iram;
+    MemoryRegion iomem1;
+    MemoryRegion d_flash;
+    MemoryRegion iomem2;
+    MemoryRegion iomem3;
+    MemoryRegion c_flash;
+    qemu_irq irq[256];
+} RX62NState;
+
+#endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 10/11] Add rx-softmmu
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (8 preceding siblings ...)
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 09/11] RX Target hardware definition Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 11/11] MAINTAINERS: Add RX entry Yoshinori Sato
                     ` (3 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 arch_init.c                    | 2 ++
 configure                      | 8 ++++++++
 default-configs/rx-softmmu.mak | 7 +++++++
 include/sysemu/arch_init.h     | 1 +
 4 files changed, 18 insertions(+)
 create mode 100644 default-configs/rx-softmmu.mak

diff --git a/arch_init.c b/arch_init.c
index f4f3f610c8..cc25ddd7ca 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -74,6 +74,8 @@ int graphic_depth = 32;
 #define QEMU_ARCH QEMU_ARCH_PPC
 #elif defined(TARGET_RISCV)
 #define QEMU_ARCH QEMU_ARCH_RISCV
+#elif defined(TARGET_RX)
+#define QEMU_ARCH QEMU_ARCH_RX
 #elif defined(TARGET_S390X)
 #define QEMU_ARCH QEMU_ARCH_S390X
 #elif defined(TARGET_SH4)
diff --git a/configure b/configure
index 540bee19ba..6bae0d4e97 100755
--- a/configure
+++ b/configure
@@ -7306,6 +7306,11 @@ case "$target_name" in
     mttcg=yes
     target_compiler=$cross_cc_riscv64
   ;;
+  rx)
+    TARGET_ARCH=rx
+    bflt="yes"
+    target_compiler=$cross_cc_rx
+  ;;
   sh4|sh4eb)
     TARGET_ARCH=sh4
     bflt="yes"
@@ -7526,6 +7531,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
   riscv*)
     disas_config "RISCV"
   ;;
+  rx)
+    disas_config "RX"
+  ;;
   s390*)
     disas_config "S390"
   ;;
diff --git a/default-configs/rx-softmmu.mak b/default-configs/rx-softmmu.mak
new file mode 100644
index 0000000000..0aaa8d4332
--- /dev/null
+++ b/default-configs/rx-softmmu.mak
@@ -0,0 +1,7 @@
+# Default configuration for rx-softmmu
+
+CONFIG_SERIAL=y
+CONFIG_PTIMER=y
+CONFIG_RX=y
+CONFIG_RENESAS_SCI=y
+CONFIG_RX_DIS=y
diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
index 10cbafe970..3f4f844f7b 100644
--- a/include/sysemu/arch_init.h
+++ b/include/sysemu/arch_init.h
@@ -25,6 +25,7 @@ enum {
     QEMU_ARCH_NIOS2 = (1 << 17),
     QEMU_ARCH_HPPA = (1 << 18),
     QEMU_ARCH_RISCV = (1 << 19),
+    QEMU_ARCH_RX = (1 << 20),
 };
 
 extern const uint32_t arch_type;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH RFC v3 11/11] MAINTAINERS: Add RX entry.
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (9 preceding siblings ...)
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 10/11] Add rx-softmmu Yoshinori Sato
@ 2019-03-02  6:21   ` Yoshinori Sato
  2019-03-02 19:03     ` Philippe Mathieu-Daudé
  2019-03-02  6:37   ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support no-reply
                     ` (2 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-02  6:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: Yoshinori Sato, peter.maydell, richard.henderson

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 MAINTAINERS | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 5040d9dfb1..141c4994b9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -270,6 +270,14 @@ F: include/hw/riscv/
 F: linux-user/host/riscv32/
 F: linux-user/host/riscv64/
 
+RX
+M: Yoshinori Sato <ysato@users.sourceforge.jp>
+S: Maintained
+F: target/rx/
+F: hw/rx/
+F: include/hw/rx/
+F: disas/rx.c
+
 S390
 M: Richard Henderson <rth@twiddle.net>
 M: David Hildenbrand <david@redhat.com>
@@ -1084,6 +1092,18 @@ F: pc-bios/canyonlands.dt[sb]
 F: pc-bios/u-boot-sam460ex-20100605.bin
 F: roms/u-boot-sam460ex
 
+RX Machines
+-----------
+RX-QEMU
+M: Yoshinori Sato <ysato@users.sourceforge.jp>
+S: Maintained
+F: hw/rx/rxqemu.c
+F: hw/intc/rx_icu.c
+F: hw/timer/renesas_*.c
+F: hw/char/renesas_sci.c
+F: include/hw/timer/renesas_*.h
+F: include/hw/char/renesas_sci.h
+
 SH4 Machines
 ------------
 R2D
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (10 preceding siblings ...)
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 11/11] MAINTAINERS: Add RX entry Yoshinori Sato
@ 2019-03-02  6:37   ` no-reply
  2019-03-02 18:51   ` Philippe Mathieu-Daudé
  2019-03-08  1:24   ` Richard Henderson
  13 siblings, 0 replies; 20+ messages in thread
From: no-reply @ 2019-03-02  6:37 UTC (permalink / raw)
  To: ysato; +Cc: fam, qemu-devel, peter.maydell, richard.henderson

Patchew URL: https://patchew.org/QEMU/20190302062138.10713-1-ysato@users.sourceforge.jp/



Hi,

This series seems to have some coding style problems. See output below for
more information:

Type: series
Message-id: 20190302062138.10713-1-ysato@users.sourceforge.jp
Subject: [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support

=== TEST SCRIPT BEGIN ===
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
 * [new tag]               patchew/20190302062138.10713-1-ysato@users.sourceforge.jp -> patchew/20190302062138.10713-1-ysato@users.sourceforge.jp
Switched to a new branch 'test'
e60fbb35e6 MAINTAINERS: Add RX entry.
3bdc16f591 Add rx-softmmu
38c0424e72 RX Target hardware definition
a6ec852164 RX62N internal serial communication interface
179739cb93 RX62N internal timer modules
52c62d9d30 RX62N interrupt contorol uint
cee296b1c3 target/rx: miscellaneous functions
d2050c0e82 target/rx: RX disassembler
823a75d919 target/rx: CPU definition
d5927fc378 target/rx: TCG helper
6c80d5cc68 target/rx: TCG Translation

=== OUTPUT BEGIN ===
1/11 Checking commit 6c80d5cc686a (target/rx: TCG Translation)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#15: 
new file mode 100644

ERROR: spaces required around that '*' (ctx:WxV)
#2089: FILE: target/rx/translate.c:1728:
+static bool trans_FCMP_ri(DisasContext *ctx, arg_FCMP_ri *a)
                                                          ^

ERROR: spaces required around that '*' (ctx:WxV)
#2111: FILE: target/rx/translate.c:1750:
+static bool trans_ITOF(DisasContext *ctx, arg_ITOF *a)
                                                    ^

ERROR: spaces required around that '*' (ctx:WxV)
#2232: FILE: target/rx/translate.c:1871:
+static bool trans_BNOT_lr(DisasContext *ctx, arg_BNOT_lr *a)
                                                          ^

total: 3 errors, 1 warnings, 2556 lines checked

Patch 1/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

2/11 Checking commit d5927fc378e5 (target/rx: TCG helper)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#11: 
new file mode 100644

total: 0 errors, 1 warnings, 893 lines checked

Patch 2/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
3/11 Checking commit 823a75d91989 (target/rx: CPU definition)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#11: 
new file mode 100644

total: 0 errors, 1 warnings, 490 lines checked

Patch 3/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
4/11 Checking commit d2050c0e8217 (target/rx: RX disassembler)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#34: 
new file mode 100644

total: 0 errors, 1 warnings, 1587 lines checked

Patch 4/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
5/11 Checking commit cee296b1c31c (target/rx: miscellaneous functions)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#11: 
new file mode 100644

total: 0 errors, 1 warnings, 162 lines checked

Patch 5/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
6/11 Checking commit 52c62d9d30d9 (RX62N interrupt contorol uint)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#24: 
new file mode 100644

total: 0 errors, 1 warnings, 376 lines checked

Patch 6/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
7/11 Checking commit 179739cb93ee (RX62N internal timer modules)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#30: 
new file mode 100644

total: 0 errors, 1 warnings, 730 lines checked

Patch 7/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
8/11 Checking commit a6ec852164ae (RX62N internal serial communication interface)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#28: 
new file mode 100644

total: 0 errors, 1 warnings, 338 lines checked

Patch 8/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
9/11 Checking commit 38c0424e72dd (RX Target hardware definition)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#14: 
new file mode 100644

total: 0 errors, 1 warnings, 389 lines checked

Patch 9/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
10/11 Checking commit 3bdc16f591b9 (Add rx-softmmu)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#50: 
new file mode 100644

total: 0 errors, 1 warnings, 42 lines checked

Patch 10/11 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
11/11 Checking commit e60fbb35e686 (MAINTAINERS: Add RX entry.)
=== OUTPUT END ===

Test command exited with code: 1


The full log is available at
http://patchew.org/logs/20190302062138.10713-1-ysato@users.sourceforge.jp/testing.checkpatch/?type=message.
---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to patchew-devel@redhat.com

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (11 preceding siblings ...)
  2019-03-02  6:37   ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support no-reply
@ 2019-03-02 18:51   ` Philippe Mathieu-Daudé
  2019-03-03  8:39     ` Yoshinori Sato
  2019-03-08  1:24   ` Richard Henderson
  13 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-03-02 18:51 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel; +Cc: peter.maydell, richard.henderson

Hi Yoshinori,

On 3/2/19 7:21 AM, Yoshinori Sato wrote:
> Hello.
> This patch series is added Renesas RX target emulation.
> 
> My git repository is bellow.
> git://git.pf.osdn.net/gitroot/y/ys/ysato/qemu.git
> 
> Since my understanding is not enough,
> I want many comments to make this a good one.

OK :)

Can you provide notes about how to test your port?
Such: links to toolchains, how to build, what firmware/OS we can run...

> 
> Thanks.
> 
> Changes v2
> Rewrite translate. using decodetree.py
> 
> Yoshinori Sato (11):
>   target/rx: TCG Translation
>   target/rx: TCG helper
>   target/rx: CPU definition
>   target/rx: RX disassembler
>   target/rx: miscellaneous functions
>   RX62N interrupt contorol uint
>   RX62N internal timer modules
>   RX62N internal serial communication interface
>   RX Target hardware definition
>   Add rx-softmmu
>   MAINTAINERS: Add RX entry.
> 
>  MAINTAINERS                    |   20 +
>  arch_init.c                    |    2 +
>  configure                      |    8 +
>  default-configs/rx-softmmu.mak |    7 +
>  hw/char/Makefile.objs          |    2 +-
>  hw/char/renesas_sci.c          |  288 ++++++
>  hw/intc/Makefile.objs          |    1 +
>  hw/intc/rx_icu.c               |  323 ++++++
>  hw/rx/Makefile.objs            |    1 +
>  hw/rx/rx62n.c                  |  227 ++++
>  hw/rx/rxqemu.c                 |  100 ++
>  hw/timer/Makefile.objs         |    2 +
>  hw/timer/renesas_cmt.c         |  235 +++++
>  hw/timer/renesas_tmr.c         |  412 ++++++++
>  include/disas/bfd.h            |    5 +
>  include/hw/char/renesas_sci.h  |   42 +
>  include/hw/intc/rx_icu.h       |   49 +
>  include/hw/rx/rx.h             |    7 +
>  include/hw/rx/rx62n.h          |   54 +
>  include/hw/timer/renesas_cmt.h |   33 +
>  include/hw/timer/renesas_tmr.h |   42 +
>  include/sysemu/arch_init.h     |    1 +
>  target/rx/Makefile.objs        |   11 +
>  target/rx/cpu-qom.h            |   52 +
>  target/rx/cpu.c                |  224 ++++
>  target/rx/cpu.h                |  214 ++++
>  target/rx/disas.c              | 1570 ++++++++++++++++++++++++++++
>  target/rx/gdbstub.c            |  113 ++
>  target/rx/helper.c             |  252 +++++
>  target/rx/helper.h             |   39 +
>  target/rx/insns.decode         |  336 ++++++
>  target/rx/monitor.c            |   38 +
>  target/rx/op_helper.c          |  602 +++++++++++
>  target/rx/translate.c          | 2220 ++++++++++++++++++++++++++++++++++++++++
>  34 files changed, 7531 insertions(+), 1 deletion(-)
>  create mode 100644 default-configs/rx-softmmu.mak
>  create mode 100644 hw/char/renesas_sci.c
>  create mode 100644 hw/intc/rx_icu.c
>  create mode 100644 hw/rx/Makefile.objs
>  create mode 100644 hw/rx/rx62n.c
>  create mode 100644 hw/rx/rxqemu.c
>  create mode 100644 hw/timer/renesas_cmt.c
>  create mode 100644 hw/timer/renesas_tmr.c
>  create mode 100644 include/hw/char/renesas_sci.h
>  create mode 100644 include/hw/intc/rx_icu.h
>  create mode 100644 include/hw/rx/rx.h
>  create mode 100644 include/hw/rx/rx62n.h
>  create mode 100644 include/hw/timer/renesas_cmt.h
>  create mode 100644 include/hw/timer/renesas_tmr.h
>  create mode 100644 target/rx/Makefile.objs
>  create mode 100644 target/rx/cpu-qom.h
>  create mode 100644 target/rx/cpu.c
>  create mode 100644 target/rx/cpu.h
>  create mode 100644 target/rx/disas.c
>  create mode 100644 target/rx/gdbstub.c
>  create mode 100644 target/rx/helper.c
>  create mode 100644 target/rx/helper.h
>  create mode 100644 target/rx/insns.decode
>  create mode 100644 target/rx/monitor.c
>  create mode 100644 target/rx/op_helper.c
>  create mode 100644 target/rx/translate.c
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v3 11/11] MAINTAINERS: Add RX entry.
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 11/11] MAINTAINERS: Add RX entry Yoshinori Sato
@ 2019-03-02 19:03     ` Philippe Mathieu-Daudé
  2019-03-03 13:40       ` Yoshinori Sato
  0 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-03-02 19:03 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel; +Cc: peter.maydell, richard.henderson

On 3/2/19 7:21 AM, Yoshinori Sato wrote:
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  MAINTAINERS | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5040d9dfb1..141c4994b9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -270,6 +270,14 @@ F: include/hw/riscv/
>  F: linux-user/host/riscv32/
>  F: linux-user/host/riscv64/
>  
> +RX

RX sounds very generic, what about:

  Renesas Extreme (RX)

Or

  RX (Renesas Extreme)

> +M: Yoshinori Sato <ysato@users.sourceforge.jp>
> +S: Maintained
> +F: target/rx/
> +F: hw/rx/
> +F: include/hw/rx/
> +F: disas/rx.c
> +
>  S390
>  M: Richard Henderson <rth@twiddle.net>
>  M: David Hildenbrand <david@redhat.com>
> @@ -1084,6 +1092,18 @@ F: pc-bios/canyonlands.dt[sb]
>  F: pc-bios/u-boot-sam460ex-20100605.bin
>  F: roms/u-boot-sam460ex
>  
> +RX Machines
> +-----------
> +RX-QEMU
> +M: Yoshinori Sato <ysato@users.sourceforge.jp>
> +S: Maintained
> +F: hw/rx/rxqemu.c
> +F: hw/intc/rx_icu.c
> +F: hw/timer/renesas_*.c
> +F: hw/char/renesas_sci.c
> +F: include/hw/timer/renesas_*.h
> +F: include/hw/char/renesas_sci.h
> +
>  SH4 Machines
>  ------------
>  R2D
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v3 08/11] RX62N internal serial communication interface
  2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 08/11] RX62N internal serial communication interface Yoshinori Sato
@ 2019-03-02 19:21     ` Philippe Mathieu-Daudé
  2019-03-03 13:50       ` Yoshinori Sato
  0 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-03-02 19:21 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel; +Cc: peter.maydell, richard.henderson

Hi Yoshinori,

On 3/2/19 7:21 AM, Yoshinori Sato wrote:
> This module supported only non FIFO type.
> Hardware manual.
> https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf?key=086621e01bd70347c18ea7f794aa9cc3

This link also works without the trailing
"?key=086621e01bd70347c18ea7f794aa9cc3".

> 
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  hw/char/Makefile.objs         |   2 +-
>  hw/char/renesas_sci.c         | 288 ++++++++++++++++++++++++++++++++++++++++++
>  include/hw/char/renesas_sci.h |  42 ++++++

QEMU provides a git config helper to improve git diff by showing headers
first and C sources after: scripts/git.orderfile
I recommend you to look at it.

I'll review the header, then back to source.

>  3 files changed, 331 insertions(+), 1 deletion(-)
>  create mode 100644 hw/char/renesas_sci.c
>  create mode 100644 include/hw/char/renesas_sci.h
> 
> diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
> index c4947d7ae7..68eae7b9a5 100644
> --- a/hw/char/Makefile.objs
> +++ b/hw/char/Makefile.objs
> @@ -15,7 +15,7 @@ common-obj-$(CONFIG_CADENCE) += cadence_uart.o
>  obj-$(CONFIG_EXYNOS4) += exynos4210_uart.o
>  obj-$(CONFIG_COLDFIRE) += mcf_uart.o
>  obj-$(CONFIG_OMAP) += omap_uart.o
> -obj-$(CONFIG_SH4) += sh_serial.o
> +obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o
>  obj-$(CONFIG_PSERIES) += spapr_vty.o
>  obj-$(CONFIG_DIGIC) += digic-uart.o
>  obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
> diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c
> new file mode 100644
> index 0000000000..56d070a329
> --- /dev/null
> +++ b/hw/char/renesas_sci.c
> @@ -0,0 +1,288 @@
> +/*
> + * Renesas Serial Communication Interface

I'd add here:

Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
(Rev.1.40 R01UH0033EJ0140)

> + *
> + * Copyright (c) 2019 Yoshinori Sato
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu-common.h"
> +#include "cpu.h"
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/char/renesas_sci.h"
> +#include "qemu/error-report.h"
> +
> +#define freq_to_ns(freq) (1000000000LL / freq)

You can directly use NANOSECONDS_PER_SECOND in update_trtime().

> +
> +static int can_receive(void *opaque)
> +{
> +    RSCIState *sci = RSCI(opaque);
> +    if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
> +        return 0;
> +    } else {
> +        return sci->scr & 0x10;

It is way easier to review a code using definitions generated by the
"hw/registerfields.h" API, a recent example is hw/char/cmsdk-apb-uart.c.

> +    }
> +}
> +
> +static void receive(void *opaque, const uint8_t *buf, int size)
> +{
> +    RSCIState *sci = RSCI(opaque);
> +    sci->rdr = buf[0];
> +    if (sci->ssr & 0x40 || size > 1) {
> +        sci->ssr |= 0x20;
> +        if (sci->scr & 0x40) {
> +            qemu_set_irq(sci->irq[ERI], 1);
> +        }
> +    } else {
> +        sci->ssr |= 0x40;
> +        sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime;
> +        if (sci->scr & 0x40) {
> +            qemu_set_irq(sci->irq[RXI], 1);
> +            qemu_set_irq(sci->irq[RXI], 0);

Both lines are equivalent to:

               qemu_irq_pulse(sci->irq[RXI]);

> +        }
> +    }
> +}
> +
> +static void send_byte(RSCIState *sci)
> +{
> +    if (qemu_chr_fe_backend_connected(&sci->chr)) {
> +        qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1);
> +    }
> +    timer_mod(sci->timer,
> +              qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime);
> +    sci->ssr &= ~0x04;
> +    sci->ssr |= 0x80;
> +    qemu_set_irq(sci->irq[TEI], 0);
> +    if (sci->scr & 0x80) {
> +        qemu_set_irq(sci->irq[TXI], 1);
> +        qemu_set_irq(sci->irq[TXI], 0);
> +    }
> +}
> +
> +static void txend(void *opaque)
> +{
> +    RSCIState *sci = RSCI(opaque);
> +    if ((sci->ssr & 0x80) == 0) {
> +        send_byte(sci);
> +    } else {
> +        sci->ssr |= 0x04;
> +        if (sci->scr & 0x04) {
> +            qemu_set_irq(sci->irq[TEI], 1);
> +        }
> +    }
> +}
> +
> +static void update_trtime(RSCIState *sci)
> +{
> +    static const int div[] = {1, 4, 16, 64};
> +    int w;
> +
> +    w = (sci->smr & 0x40) ? 7 : 8;      /* CHR */
> +    w += (sci->smr >> 5) & 1;           /* PE */
> +    w += (sci->smr & 0x08) ? 2 : 1;     /* STOP */
> +    sci->trtime = w * freq_to_ns(sci->input_freq) *
> +        32 * div[sci->smr & 0x03] * sci->brr;

Or:

       sci->trtime   = (sci->smr & 0x40) ? 7 : 8;
       sci->trtime  += (sci->smr >> 5) & 1;
       sci->trtime  += (sci->smr & 0x08) ? 2 : 1;
       sci->trtime  *= 32 * sci->brr;
       sci->trtime <<= 2 * (sci->smr & 0x03);
       sci->trtime  *= NANOSECONDS_PER_SECOND;
       sci->trtime  /= sci->input_freq;

> +}
> +
> +static void sci_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    hwaddr offset = addr & 0x07;

You create the region with memory_region_init_io(size=8), so no need to &=7.

> +    RSCIState *sci = RSCI(opaque);
> +    int error = 0;
> +
> +    switch (offset) {
> +    case 0: /* SMR */
> +        if ((sci->scr & 0x30) == 0) {
> +            sci->smr = val;
> +            update_trtime(sci);
> +        }
> +        break;
> +    case 1: /* BRR */
> +        if ((sci->scr & 0x30) == 0) {
> +            sci->brr = val;
> +            update_trtime(sci);
> +        }
> +        break;
> +    case 2: /* SCR */
> +        sci->scr = val;
> +        if (sci->scr & 0x20) {
> +            sci->ssr |= 0x84;
> +            qemu_set_irq(sci->irq[TXI], 1);
> +            qemu_set_irq(sci->irq[TXI], 0);
> +        }
> +        if ((sci->scr & 0x04) == 0) {
> +            qemu_set_irq(sci->irq[TEI], 0);
> +        }
> +        if ((sci->scr & 0x40) == 0) {
> +            qemu_set_irq(sci->irq[ERI], 0);
> +        }
> +        break;
> +    case 3: /* TDR */
> +        sci->tdr = val;
> +        if (sci->ssr & 0x04) {
> +            send_byte(sci);
> +        } else{
> +            sci->ssr &= ~0x80;
> +        }
> +        break;
> +    case 4: /* SSR */
> +        sci->ssr &= ~0x38 | (val & 0x38);

This expression is not clear... Don't you want:

           sci->ssr &= ~0x38;
           sci->ssr |= val & 0x38;

> +        if (((sci->read_ssr & 0x38) ^ (sci->ssr & 0x38)) &&
> +            (sci->ssr & 0x38) == 0) {

Is this equivalent to:

           if ((sci->ssr & 0x38) == 0 && (sci->read_ssr & 0x38) != 0) {

> +            qemu_set_irq(sci->irq[ERI], 0);
> +        }
> +        break;
> +    case 5: /* RDR */
> +        error = 1; break;

Here error is due to read-only register, QEMU provides:

           qemu_log_mask(LOG_GUEST_ERROR, "Read only register: "...

> +    case 6: /* SCMR */
> +        sci->scmr = val; break;
> +    case 7: /* SEMR */
> +        sci->semr = val; break;
> +    }
> +
> +    if (error) {
> +        error_report("rsci: unsupported write request to %08lx", addr);

"unsupported" is not very clear, for unimplemented you can use:

           qemu_log_mask(LOG_UNIMP,
                         "Register XX not implemented\n");

> +    }
> +}
> +
> +static uint64_t sci_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    hwaddr offset = addr & 0x07;
> +    RSCIState *sci = RSCI(opaque);
> +    int error = 0;
> +    switch (offset) {
> +    case 0: /* SMR */
> +        return sci->smr;
> +    case 1: /* BRR */
> +        return sci->brr;
> +    case 2: /* SCR */
> +        return sci->scr;
> +    case 3: /* TDR */
> +        return sci->tdr;
> +    case 4: /* SSR */
> +        sci->read_ssr = sci->ssr;
> +        return sci->ssr;
> +    case 5: /* RDR */
> +        sci->ssr &= ~0x40;
> +        return sci->rdr;
> +    case 6: /* SCMR */
> +        return sci->scmr;
> +    case 7: /* SEMR */
> +        return sci->semr;
> +    }
> +
> +    if (error) {
> +        error_report("rsci: unsupported write request to %08lx", addr);
> +    }
> +    return -1;
> +}
> +
> +static const MemoryRegionOps sci_ops = {
> +    .write = sci_write,
> +    .read  = sci_read,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .impl = {
> +        .min_access_size = 1,

You can drop the implicit ".min_access_size = 1".

> +        .max_access_size = 1,
> +    },
> +};
> +
> +static void rsci_reset(DeviceState *dev)
> +{
> +    RSCIState *sci = RSCI(dev);
> +    sci->smr = sci->scr = 0x00;
> +    sci->brr = 0xff;
> +    sci->tdr = 0xff;
> +    sci->rdr = 0x00;
> +    sci->ssr = 0x84;
> +    sci->scmr = 0x00;
> +    sci->semr = 0x00;
> +    sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +}
> +
> +static void sci_event(void *opaque, int event)
> +{
> +    RSCIState *sci = RSCI(opaque);
> +    if (event == CHR_EVENT_BREAK) {
> +        sci->ssr |= 0x10;
> +        if (sci->scr & 0x40) {
> +            qemu_set_irq(sci->irq[ERI], 1);
> +        }
> +    }
> +}
> +
> +static void rsci_realize(DeviceState *dev, Error **errp)
> +{
> +    RSCIState *sci = RSCI(dev);
> +
> +    qemu_chr_fe_set_handlers(&sci->chr, can_receive, receive,
> +                             sci_event, NULL, sci, NULL, true);

You might want to check s->input_freq != 0 here.

> +}
> +
> +static void rsci_init(Object *obj)
> +{
> +    SysBusDevice *d = SYS_BUS_DEVICE(obj);
> +    RSCIState *sci = RSCI(obj);
> +    int i;
> +
> +    memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops,
> +                          sci, "renesas-sci", 0x8);
> +    sysbus_init_mmio(d, &sci->memory);
> +
> +    for (i = 0; i < 4; i++) {

4 -> ARRAY_COUNT(sci->irq) or SCI_IRQ_COUNT.

> +        sysbus_init_irq(d, &sci->irq[i]);
> +    }
> +    sci->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, txend, sci);
> +}
> +
> +static const VMStateDescription vmstate_rcmt = {
> +    .name = "renesas-sci",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static Property rsci_properties[] = {
> +    DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0),
> +    DEFINE_PROP_CHR("chardev", RSCIState, chr),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void rsci_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = rsci_realize;
> +    dc->props = rsci_properties;
> +    dc->vmsd = &vmstate_rcmt;
> +    dc->reset = rsci_reset;
> +}
> +
> +static const TypeInfo rsci_info = {
> +    .name       = TYPE_RENESAS_SCI,
> +    .parent     = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(RSCIState),
> +    .instance_init = rsci_init,
> +    .class_init = rsci_class_init,
> +};
> +
> +static void rsci_register_types(void)
> +{
> +    type_register_static(&rsci_info);
> +}
> +
> +type_init(rsci_register_types)
> diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h
> new file mode 100644
> index 0000000000..47e3e7a5d7
> --- /dev/null
> +++ b/include/hw/char/renesas_sci.h
> @@ -0,0 +1,42 @@
> +/*
> + * Renesas Serial Communication Interface
> + *
> + * Copyright (c) 2018 Yoshinori Sato
> + *
> + * This code is licensed under the GPL version 2 or later.
> + *
> + */
> +
> +#include "chardev/char-fe.h"
> +#include "qemu/timer.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_RENESAS_SCI "renesas-sci"
> +#define RSCI(obj) OBJECT_CHECK(RSCIState, (obj), TYPE_RENESAS_SCI)
> +

Since those definitions are related, I recommend using:

enum SciIrqIndex {
    ERI = 0,
    RXI = 1,
    ...

> +#define ERI 0
> +#define RXI 1
> +#define TXI 2
> +#define TEI 3

   SCI_IRQ_COUNT = 4
};

> +
> +typedef struct {
> +    SysBusDevice parent_obj;
> +    MemoryRegion memory;
> +
> +    uint8_t smr;
> +    uint8_t brr;
> +    uint8_t scr;
> +    uint8_t tdr;
> +    uint8_t ssr;
> +    uint8_t rdr;
> +    uint8_t scmr;
> +    uint8_t semr;
> +
> +    uint8_t read_ssr;
> +    long long trtime;
> +    long long rx_next;

Can you use int64_t to keep both consistent?

> +    QEMUTimer *timer;
> +    CharBackend chr;
> +    uint64_t input_freq;
> +    qemu_irq irq[4];

Now you can use irq[SCI_IRQ_COUNT];

> +} RSCIState;
> 

Regards,

Phil.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support
  2019-03-02 18:51   ` Philippe Mathieu-Daudé
@ 2019-03-03  8:39     ` Yoshinori Sato
  0 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-03  8:39 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé; +Cc: qemu-devel, peter.maydell, richard.henderson

On Sun, 03 Mar 2019 03:51:14 +0900,
Philippe Mathieu-Daudé wrote:
> 
> Hi Yoshinori,
> 
> On 3/2/19 7:21 AM, Yoshinori Sato wrote:
> > Hello.
> > This patch series is added Renesas RX target emulation.
> > 
> > My git repository is bellow.
> > git://git.pf.osdn.net/gitroot/y/ys/ysato/qemu.git
> > 
> > Since my understanding is not enough,
> > I want many comments to make this a good one.
> 
> OK :)
> 
> Can you provide notes about how to test your port?
> Such: links to toolchains, how to build, what firmware/OS we can run...

OK.
toolchain - rx-unknown-linux
binutils-2.32 include rx-unknown-linux support.
$ configure --target=rx-unknown-linux
$ make

gcc - please use my git repo.
git://git.pf.osdn.net/gitroot/y/ys/ysato/gcc.git rx-trunk
$ configure --target=rx-unknown-linux --enable-languages=c --disable-shared \
--disable-threads --with-uclibc --disable-libssp --disable-libquadmath \
--disable-libgomp --disable-libatomic
$ make

This toolchain can build u-boot / linux.

target program.
u-boot
git://git.pf.osdn.net/gitroot/y/ys/ysato/uboot.git rx
pre build binary in bellow.
https://osdn.net/users/ysato/pf/qemu/dl/u-boot.bin

linux
git://git.osdn.net/gitroot/uclinux-h8/linux.git rx
https://osdn.net/users/ysato/pf/qemu/dl/zImage

Since linux is still incomplete, it may be problematic.

> > 
> > Thanks.
> > 
> > Changes v2
> > Rewrite translate. using decodetree.py
> > 
> > Yoshinori Sato (11):
> >   target/rx: TCG Translation
> >   target/rx: TCG helper
> >   target/rx: CPU definition
> >   target/rx: RX disassembler
> >   target/rx: miscellaneous functions
> >   RX62N interrupt contorol uint
> >   RX62N internal timer modules
> >   RX62N internal serial communication interface
> >   RX Target hardware definition
> >   Add rx-softmmu
> >   MAINTAINERS: Add RX entry.
> > 
> >  MAINTAINERS                    |   20 +
> >  arch_init.c                    |    2 +
> >  configure                      |    8 +
> >  default-configs/rx-softmmu.mak |    7 +
> >  hw/char/Makefile.objs          |    2 +-
> >  hw/char/renesas_sci.c          |  288 ++++++
> >  hw/intc/Makefile.objs          |    1 +
> >  hw/intc/rx_icu.c               |  323 ++++++
> >  hw/rx/Makefile.objs            |    1 +
> >  hw/rx/rx62n.c                  |  227 ++++
> >  hw/rx/rxqemu.c                 |  100 ++
> >  hw/timer/Makefile.objs         |    2 +
> >  hw/timer/renesas_cmt.c         |  235 +++++
> >  hw/timer/renesas_tmr.c         |  412 ++++++++
> >  include/disas/bfd.h            |    5 +
> >  include/hw/char/renesas_sci.h  |   42 +
> >  include/hw/intc/rx_icu.h       |   49 +
> >  include/hw/rx/rx.h             |    7 +
> >  include/hw/rx/rx62n.h          |   54 +
> >  include/hw/timer/renesas_cmt.h |   33 +
> >  include/hw/timer/renesas_tmr.h |   42 +
> >  include/sysemu/arch_init.h     |    1 +
> >  target/rx/Makefile.objs        |   11 +
> >  target/rx/cpu-qom.h            |   52 +
> >  target/rx/cpu.c                |  224 ++++
> >  target/rx/cpu.h                |  214 ++++
> >  target/rx/disas.c              | 1570 ++++++++++++++++++++++++++++
> >  target/rx/gdbstub.c            |  113 ++
> >  target/rx/helper.c             |  252 +++++
> >  target/rx/helper.h             |   39 +
> >  target/rx/insns.decode         |  336 ++++++
> >  target/rx/monitor.c            |   38 +
> >  target/rx/op_helper.c          |  602 +++++++++++
> >  target/rx/translate.c          | 2220 ++++++++++++++++++++++++++++++++++++++++
> >  34 files changed, 7531 insertions(+), 1 deletion(-)
> >  create mode 100644 default-configs/rx-softmmu.mak
> >  create mode 100644 hw/char/renesas_sci.c
> >  create mode 100644 hw/intc/rx_icu.c
> >  create mode 100644 hw/rx/Makefile.objs
> >  create mode 100644 hw/rx/rx62n.c
> >  create mode 100644 hw/rx/rxqemu.c
> >  create mode 100644 hw/timer/renesas_cmt.c
> >  create mode 100644 hw/timer/renesas_tmr.c
> >  create mode 100644 include/hw/char/renesas_sci.h
> >  create mode 100644 include/hw/intc/rx_icu.h
> >  create mode 100644 include/hw/rx/rx.h
> >  create mode 100644 include/hw/rx/rx62n.h
> >  create mode 100644 include/hw/timer/renesas_cmt.h
> >  create mode 100644 include/hw/timer/renesas_tmr.h
> >  create mode 100644 target/rx/Makefile.objs
> >  create mode 100644 target/rx/cpu-qom.h
> >  create mode 100644 target/rx/cpu.c
> >  create mode 100644 target/rx/cpu.h
> >  create mode 100644 target/rx/disas.c
> >  create mode 100644 target/rx/gdbstub.c
> >  create mode 100644 target/rx/helper.c
> >  create mode 100644 target/rx/helper.h
> >  create mode 100644 target/rx/insns.decode
> >  create mode 100644 target/rx/monitor.c
> >  create mode 100644 target/rx/op_helper.c
> >  create mode 100644 target/rx/translate.c
> > 
> 

-- 
Yosinori Sato

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v3 11/11] MAINTAINERS: Add RX entry.
  2019-03-02 19:03     ` Philippe Mathieu-Daudé
@ 2019-03-03 13:40       ` Yoshinori Sato
  0 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-03 13:40 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé; +Cc: qemu-devel, peter.maydell, richard.henderson

On Sun, 03 Mar 2019 04:03:19 +0900,
Philippe Mathieu-Daudé wrote:
> 
> On 3/2/19 7:21 AM, Yoshinori Sato wrote:
> > Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> > ---
> >  MAINTAINERS | 20 ++++++++++++++++++++
> >  1 file changed, 20 insertions(+)
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 5040d9dfb1..141c4994b9 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -270,6 +270,14 @@ F: include/hw/riscv/
> >  F: linux-user/host/riscv32/
> >  F: linux-user/host/riscv64/
> >  
> > +RX
> 
> RX sounds very generic, what about:
> 
>   Renesas Extreme (RX)
> 
> Or
> 
>   RX (Renesas Extreme)
> 
> > +M: Yoshinori Sato <ysato@users.sourceforge.jp>
> > +S: Maintained
> > +F: target/rx/
> > +F: hw/rx/
> > +F: include/hw/rx/
> > +F: disas/rx.c
> > +
> >  S390
> >  M: Richard Henderson <rth@twiddle.net>
> >  M: David Hildenbrand <david@redhat.com>
> > @@ -1084,6 +1092,18 @@ F: pc-bios/canyonlands.dt[sb]
> >  F: pc-bios/u-boot-sam460ex-20100605.bin
> >  F: roms/u-boot-sam460ex
> >  
> > +RX Machines
> > +-----------
> > +RX-QEMU
> > +M: Yoshinori Sato <ysato@users.sourceforge.jp>
> > +S: Maintained
> > +F: hw/rx/rxqemu.c
> > +F: hw/intc/rx_icu.c
> > +F: hw/timer/renesas_*.c
> > +F: hw/char/renesas_sci.c
> > +F: include/hw/timer/renesas_*.h
> > +F: include/hw/char/renesas_sci.h
> > +
> >  SH4 Machines
> >  ------------
> >  R2D
> > 
> 

OK.
I will update entry.
RENESAS RX

Thanks.

-- 
Yosinori Sato

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v3 08/11] RX62N internal serial communication interface
  2019-03-02 19:21     ` Philippe Mathieu-Daudé
@ 2019-03-03 13:50       ` Yoshinori Sato
  0 siblings, 0 replies; 20+ messages in thread
From: Yoshinori Sato @ 2019-03-03 13:50 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé; +Cc: qemu-devel, peter.maydell, richard.henderson

On Sun, 03 Mar 2019 04:21:10 +0900,
Philippe Mathieu-Daudé wrote:
> 
> Hi Yoshinori,
> 
> On 3/2/19 7:21 AM, Yoshinori Sato wrote:
> > This module supported only non FIFO type.
> > Hardware manual.
> > https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf?key=086621e01bd70347c18ea7f794aa9cc3
> 
> This link also works without the trailing
> "?key=086621e01bd70347c18ea7f794aa9cc3".

OK.
It is probably a parameter for searching. remove it.

> > 
> > Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> > ---
> >  hw/char/Makefile.objs         |   2 +-
> >  hw/char/renesas_sci.c         | 288 ++++++++++++++++++++++++++++++++++++++++++
> >  include/hw/char/renesas_sci.h |  42 ++++++
> 
> QEMU provides a git config helper to improve git diff by showing headers
> first and C sources after: scripts/git.orderfile
> I recommend you to look at it.

OK.

> I'll review the header, then back to source.
> 
> >  3 files changed, 331 insertions(+), 1 deletion(-)
> >  create mode 100644 hw/char/renesas_sci.c
> >  create mode 100644 include/hw/char/renesas_sci.h
> > 
> > diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
> > index c4947d7ae7..68eae7b9a5 100644
> > --- a/hw/char/Makefile.objs
> > +++ b/hw/char/Makefile.objs
> > @@ -15,7 +15,7 @@ common-obj-$(CONFIG_CADENCE) += cadence_uart.o
> >  obj-$(CONFIG_EXYNOS4) += exynos4210_uart.o
> >  obj-$(CONFIG_COLDFIRE) += mcf_uart.o
> >  obj-$(CONFIG_OMAP) += omap_uart.o
> > -obj-$(CONFIG_SH4) += sh_serial.o
> > +obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o
> >  obj-$(CONFIG_PSERIES) += spapr_vty.o
> >  obj-$(CONFIG_DIGIC) += digic-uart.o
> >  obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
> > diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c
> > new file mode 100644
> > index 0000000000..56d070a329
> > --- /dev/null
> > +++ b/hw/char/renesas_sci.c
> > @@ -0,0 +1,288 @@
> > +/*
> > + * Renesas Serial Communication Interface
> 
> I'd add here:
> 
> Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
> (Rev.1.40 R01UH0033EJ0140)

OK.

> > + *
> > + * Copyright (c) 2019 Yoshinori Sato
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qemu-common.h"
> > +#include "cpu.h"
> > +#include "hw/hw.h"
> > +#include "hw/sysbus.h"
> > +#include "hw/char/renesas_sci.h"
> > +#include "qemu/error-report.h"
> > +
> > +#define freq_to_ns(freq) (1000000000LL / freq)
> 
> You can directly use NANOSECONDS_PER_SECOND in update_trtime().

OK.

> > +
> > +static int can_receive(void *opaque)
> > +{
> > +    RSCIState *sci = RSCI(opaque);
> > +    if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
> > +        return 0;
> > +    } else {
> > +        return sci->scr & 0x10;
> 
> It is way easier to review a code using definitions generated by the
> "hw/registerfields.h" API, a recent example is hw/char/cmsdk-apb-uart.c.

OK.
Other part have same code. I will update it as well.

> > +    }
> > +}
> > +
> > +static void receive(void *opaque, const uint8_t *buf, int size)
> > +{
> > +    RSCIState *sci = RSCI(opaque);
> > +    sci->rdr = buf[0];
> > +    if (sci->ssr & 0x40 || size > 1) {
> > +        sci->ssr |= 0x20;
> > +        if (sci->scr & 0x40) {
> > +            qemu_set_irq(sci->irq[ERI], 1);
> > +        }
> > +    } else {
> > +        sci->ssr |= 0x40;
> > +        sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime;
> > +        if (sci->scr & 0x40) {
> > +            qemu_set_irq(sci->irq[RXI], 1);
> > +            qemu_set_irq(sci->irq[RXI], 0);
> 
> Both lines are equivalent to:
> 
>                qemu_irq_pulse(sci->irq[RXI]);

OK. 

> 
> > +        }
> > +    }
> > +}
> > +
> > +static void send_byte(RSCIState *sci)
> > +{
> > +    if (qemu_chr_fe_backend_connected(&sci->chr)) {
> > +        qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1);
> > +    }
> > +    timer_mod(sci->timer,
> > +              qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime);
> > +    sci->ssr &= ~0x04;
> > +    sci->ssr |= 0x80;
> > +    qemu_set_irq(sci->irq[TEI], 0);
> > +    if (sci->scr & 0x80) {
> > +        qemu_set_irq(sci->irq[TXI], 1);
> > +        qemu_set_irq(sci->irq[TXI], 0);
> > +    }
> > +}
> > +
> > +static void txend(void *opaque)
> > +{
> > +    RSCIState *sci = RSCI(opaque);
> > +    if ((sci->ssr & 0x80) == 0) {
> > +        send_byte(sci);
> > +    } else {
> > +        sci->ssr |= 0x04;
> > +        if (sci->scr & 0x04) {
> > +            qemu_set_irq(sci->irq[TEI], 1);
> > +        }
> > +    }
> > +}
> > +
> > +static void update_trtime(RSCIState *sci)
> > +{
> > +    static const int div[] = {1, 4, 16, 64};
> > +    int w;
> > +
> > +    w = (sci->smr & 0x40) ? 7 : 8;      /* CHR */
> > +    w += (sci->smr >> 5) & 1;           /* PE */
> > +    w += (sci->smr & 0x08) ? 2 : 1;     /* STOP */
> > +    sci->trtime = w * freq_to_ns(sci->input_freq) *
> > +        32 * div[sci->smr & 0x03] * sci->brr;
> 
> Or:
> 
>        sci->trtime   = (sci->smr & 0x40) ? 7 : 8;
>        sci->trtime  += (sci->smr >> 5) & 1;
>        sci->trtime  += (sci->smr & 0x08) ? 2 : 1;
>        sci->trtime  *= 32 * sci->brr;
>        sci->trtime <<= 2 * (sci->smr & 0x03);
>        sci->trtime  *= NANOSECONDS_PER_SECOND;
>        sci->trtime  /= sci->input_freq;

OK.

> > +}
> > +
> > +static void sci_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> > +{
> > +    hwaddr offset = addr & 0x07;
> 
> You create the region with memory_region_init_io(size=8), so no need to &=7.

OK.

> > +    RSCIState *sci = RSCI(opaque);
> > +    int error = 0;
> > +
> > +    switch (offset) {
> > +    case 0: /* SMR */
> > +        if ((sci->scr & 0x30) == 0) {
> > +            sci->smr = val;
> > +            update_trtime(sci);
> > +        }
> > +        break;
> > +    case 1: /* BRR */
> > +        if ((sci->scr & 0x30) == 0) {
> > +            sci->brr = val;
> > +            update_trtime(sci);
> > +        }
> > +        break;
> > +    case 2: /* SCR */
> > +        sci->scr = val;
> > +        if (sci->scr & 0x20) {
> > +            sci->ssr |= 0x84;
> > +            qemu_set_irq(sci->irq[TXI], 1);
> > +            qemu_set_irq(sci->irq[TXI], 0);
> > +        }
> > +        if ((sci->scr & 0x04) == 0) {
> > +            qemu_set_irq(sci->irq[TEI], 0);
> > +        }
> > +        if ((sci->scr & 0x40) == 0) {
> > +            qemu_set_irq(sci->irq[ERI], 0);
> > +        }
> > +        break;
> > +    case 3: /* TDR */
> > +        sci->tdr = val;
> > +        if (sci->ssr & 0x04) {
> > +            send_byte(sci);
> > +        } else{
> > +            sci->ssr &= ~0x80;
> > +        }
> > +        break;
> > +    case 4: /* SSR */
> > +        sci->ssr &= ~0x38 | (val & 0x38);
> 
> This expression is not clear... Don't you want:
> 
>            sci->ssr &= ~0x38;
>            sci->ssr |= val & 0x38;

Yes.
It more clearly.

> > +        if (((sci->read_ssr & 0x38) ^ (sci->ssr & 0x38)) &&
> > +            (sci->ssr & 0x38) == 0) {
> 
> Is this equivalent to:
> 
>            if ((sci->ssr & 0x38) == 0 && (sci->read_ssr & 0x38) != 0) {

OK.

> > +            qemu_set_irq(sci->irq[ERI], 0);
> > +        }
> > +        break;
> > +    case 5: /* RDR */
> > +        error = 1; break;
> 
> Here error is due to read-only register, QEMU provides:
> 
>            qemu_log_mask(LOG_GUEST_ERROR, "Read only register: "...

OK.

> > +    case 6: /* SCMR */
> > +        sci->scmr = val; break;
> > +    case 7: /* SEMR */
> > +        sci->semr = val; break;
> > +    }
> > +
> > +    if (error) {
> > +        error_report("rsci: unsupported write request to %08lx", addr);
> 
> "unsupported" is not very clear, for unimplemented you can use:
> 
>            qemu_log_mask(LOG_UNIMP,
>                          "Register XX not implemented\n");

OK.

> > +    }
> > +}
> > +
> > +static uint64_t sci_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    hwaddr offset = addr & 0x07;
> > +    RSCIState *sci = RSCI(opaque);
> > +    int error = 0;
> > +    switch (offset) {
> > +    case 0: /* SMR */
> > +        return sci->smr;
> > +    case 1: /* BRR */
> > +        return sci->brr;
> > +    case 2: /* SCR */
> > +        return sci->scr;
> > +    case 3: /* TDR */
> > +        return sci->tdr;
> > +    case 4: /* SSR */
> > +        sci->read_ssr = sci->ssr;
> > +        return sci->ssr;
> > +    case 5: /* RDR */
> > +        sci->ssr &= ~0x40;
> > +        return sci->rdr;
> > +    case 6: /* SCMR */
> > +        return sci->scmr;
> > +    case 7: /* SEMR */
> > +        return sci->semr;
> > +    }
> > +
> > +    if (error) {
> > +        error_report("rsci: unsupported write request to %08lx", addr);
> > +    }
> > +    return -1;
> > +}
> > +
> > +static const MemoryRegionOps sci_ops = {
> > +    .write = sci_write,
> > +    .read  = sci_read,
> > +    .endianness = DEVICE_NATIVE_ENDIAN,
> > +    .impl = {
> > +        .min_access_size = 1,
> 
> You can drop the implicit ".min_access_size = 1".

OK.

> > +        .max_access_size = 1,
> > +    },
> > +};
> > +
> > +static void rsci_reset(DeviceState *dev)
> > +{
> > +    RSCIState *sci = RSCI(dev);
> > +    sci->smr = sci->scr = 0x00;
> > +    sci->brr = 0xff;
> > +    sci->tdr = 0xff;
> > +    sci->rdr = 0x00;
> > +    sci->ssr = 0x84;
> > +    sci->scmr = 0x00;
> > +    sci->semr = 0x00;
> > +    sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > +}
> > +
> > +static void sci_event(void *opaque, int event)
> > +{
> > +    RSCIState *sci = RSCI(opaque);
> > +    if (event == CHR_EVENT_BREAK) {
> > +        sci->ssr |= 0x10;
> > +        if (sci->scr & 0x40) {
> > +            qemu_set_irq(sci->irq[ERI], 1);
> > +        }
> > +    }
> > +}
> > +
> > +static void rsci_realize(DeviceState *dev, Error **errp)
> > +{
> > +    RSCIState *sci = RSCI(dev);
> > +
> > +    qemu_chr_fe_set_handlers(&sci->chr, can_receive, receive,
> > +                             sci_event, NULL, sci, NULL, true);
> 
> You might want to check s->input_freq != 0 here.

OK.

> > +}
> > +
> > +static void rsci_init(Object *obj)
> > +{
> > +    SysBusDevice *d = SYS_BUS_DEVICE(obj);
> > +    RSCIState *sci = RSCI(obj);
> > +    int i;
> > +
> > +    memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops,
> > +                          sci, "renesas-sci", 0x8);
> > +    sysbus_init_mmio(d, &sci->memory);
> > +
> > +    for (i = 0; i < 4; i++) {
> 
> 4 -> ARRAY_COUNT(sci->irq) or SCI_IRQ_COUNT.

OK.

> > +        sysbus_init_irq(d, &sci->irq[i]);
> > +    }
> > +    sci->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, txend, sci);
> > +}
> > +
> > +static const VMStateDescription vmstate_rcmt = {
> > +    .name = "renesas-sci",
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_END_OF_LIST()
> > +    }
> > +};
> > +
> > +static Property rsci_properties[] = {
> > +    DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0),
> > +    DEFINE_PROP_CHR("chardev", RSCIState, chr),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void rsci_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > +    dc->realize = rsci_realize;
> > +    dc->props = rsci_properties;
> > +    dc->vmsd = &vmstate_rcmt;
> > +    dc->reset = rsci_reset;
> > +}
> > +
> > +static const TypeInfo rsci_info = {
> > +    .name       = TYPE_RENESAS_SCI,
> > +    .parent     = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size = sizeof(RSCIState),
> > +    .instance_init = rsci_init,
> > +    .class_init = rsci_class_init,
> > +};
> > +
> > +static void rsci_register_types(void)
> > +{
> > +    type_register_static(&rsci_info);
> > +}
> > +
> > +type_init(rsci_register_types)
> > diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h
> > new file mode 100644
> > index 0000000000..47e3e7a5d7
> > --- /dev/null
> > +++ b/include/hw/char/renesas_sci.h
> > @@ -0,0 +1,42 @@
> > +/*
> > + * Renesas Serial Communication Interface
> > + *
> > + * Copyright (c) 2018 Yoshinori Sato
> > + *
> > + * This code is licensed under the GPL version 2 or later.
> > + *
> > + */
> > +
> > +#include "chardev/char-fe.h"
> > +#include "qemu/timer.h"
> > +#include "hw/sysbus.h"
> > +
> > +#define TYPE_RENESAS_SCI "renesas-sci"
> > +#define RSCI(obj) OBJECT_CHECK(RSCIState, (obj), TYPE_RENESAS_SCI)
> > +
> 
> Since those definitions are related, I recommend using:
> 
> enum SciIrqIndex {
>     ERI = 0,
>     RXI = 1,
>     ...
> 
> > +#define ERI 0
> > +#define RXI 1
> > +#define TXI 2
> > +#define TEI 3
> 
>    SCI_IRQ_COUNT = 4
> };

OK.

> > +
> > +typedef struct {
> > +    SysBusDevice parent_obj;
> > +    MemoryRegion memory;
> > +
> > +    uint8_t smr;
> > +    uint8_t brr;
> > +    uint8_t scr;
> > +    uint8_t tdr;
> > +    uint8_t ssr;
> > +    uint8_t rdr;
> > +    uint8_t scmr;
> > +    uint8_t semr;
> > +
> > +    uint8_t read_ssr;
> > +    long long trtime;
> > +    long long rx_next;
> 
> Can you use int64_t to keep both consistent?

OK.

> > +    QEMUTimer *timer;
> > +    CharBackend chr;
> > +    uint64_t input_freq;
> > +    qemu_irq irq[4];
> 
> Now you can use irq[SCI_IRQ_COUNT];

OK.

> > +} RSCIState;
> > 
> 
> Regards,
> 
> Phil.
> 

Your comment is very useful.
Thank you.

-- 
Yosinori Sato

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support
  2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
                     ` (12 preceding siblings ...)
  2019-03-02 18:51   ` Philippe Mathieu-Daudé
@ 2019-03-08  1:24   ` Richard Henderson
  13 siblings, 0 replies; 20+ messages in thread
From: Richard Henderson @ 2019-03-08  1:24 UTC (permalink / raw)
  To: Yoshinori Sato, qemu-devel; +Cc: peter.maydell

On 3/1/19 10:21 PM, Yoshinori Sato wrote:
> My git repository is bellow.
> git://git.pf.osdn.net/gitroot/y/ys/ysato/qemu.git

Somehow patch 1 did not arrive, so I am reviewing based on
rebasing this branch against master, and then looking at the diff.

> +struct CCop;

Unused?

> +static inline void cpu_get_tb_cpu_state(CPURXState *env, target_ulong *pc,
> +                                        target_ulong *cs_base, uint32_t *flags)
> +{
> +    *pc = env->pc;
> +    *cs_base = 0;
> +    *flags = 0;
> +}

*flags should contain PSW[PM], I think, so that knowledge of the
privilege level is given to the translator.

Looking forward I see that you're currently testing cpu_psw_pm dynamically for
instructions that require privs, so what you have is not wrong, but this is
exactly the sort of thing that TB flags are for.

> +#define RX_PSW_OP_NEG 4

Unused?

> +#define RX_BYTE 0
> +#define RX_WORD 1
> +#define RX_LONG 2

Redundant with TCGMemOps: MO_8, MO_16, MO_32?

> +++ b/target/rx/insns.decode

Should have a copyright + license notice.

> +BCnd_b         0010 cd:4 dsp:8                         &bcnd
...
> +#BRA_b         0010 1110 dsp:8         # overlaps BCnd_b

FYI, using pattern groups this can be written

{
  BRA_b        0010 1110 dsp:8
  Bcnd_b       0010 cd:4 dsp:8
}

I expect to have pattern groups merged this week.

> +static void rx_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
> +{
> +    DisasContext *ctx = container_of(dcbase, DisasContext, base);
> +
> +    ctx->src = tcg_temp_new();
> +}

This allocation will not survive across a branch or label.
So, after any SHIFTR_REG, for instance.

I think you need to allocate this on demand each insn, and
free it as necessary after each insn.

(Although avoiding branches in tcg is always helpful.)

> +/* generic load / store wrapper */
> +static inline void rx_gen_ldst(unsigned int size, unsigned int dir,
> +                        TCGv reg, TCGv mem)
> +{
> +    if (dir) {
> +        tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_SIGN | MO_TE);
> +    } else {
> +        tcg_gen_qemu_st_i32(reg, mem, 0, size | MO_TE);
> +    }
> +}

It would probably be worthwhile to split this function, and drop the "dir"
parameter.  It is always a constant, so instead of

  rx_gen_ldst(mi, RX_MEMORY_LD, ctx->src, ctx->src);
  rx_gen_ldst(a->sz, RX_MEMORY_ST, cpu_regs[a->rs], ctx->src);

use the shorter

  rx_gen_ld(mi, ctx->src, ctx->src);
  rx_gen_st(a->sz, cpu_regs[a->rs], ctx->src);


> +/* mov.l #uimm4,rd */
> +/* mov.l #uimm8,rd */
> +static bool trans_MOV_ri(DisasContext *ctx, arg_MOV_ri *a)
> +{
> +    tcg_gen_movi_i32(cpu_regs[a->rd], a->imm & 0xff);
> +    return true;
> +}

a->imm will already have been masked by the decode.
You can drop the & 0xff here.

> +/* mov.l #imm,rd */
> +static bool trans_MOV_rli(DisasContext *ctx, arg_MOV_rli *a)
> +{
> +    tcg_gen_movi_i32(cpu_regs[a->rd], a->imm);
> +    return true;
> +}

As written, this function is redundant.  We should be using the same MOV_ri,
with the immediate loaded from %b2_li_2.

Likewise for trans_MOV_mi vs trans_MOV_mli.  That said...

> +static bool trans_MOV_mli(DisasContext *ctx, arg_MOV_mli *a)
> +{
> +    TCGv imm = tcg_const_i32(a->imm);
> +    if (a->ld == 2) {
> +        a->dsp = bswap_16(a->dsp);
> +    }

This suggests that the decode is incorrect.  Or perhaps the construction of the
32-bit insn passed into decode.  In decode_load_bytes, we construct a
big-endian value, so it would seem this dsp field should be loaded as a
little-endian value.

This could be fixed by not attempting to load the LI constant in decodetree
(for this insn), which would in turn not require that you decode the LD operand
by hand in decodetree.  E.g.

static bool trans_MOV_mli(DisasContext *ctx, arg_MOV_mli *a)
{
    TCGv imm;

    /* dsp operand comes before immediate operand */
    rx_index_addr(a->ld, a->sz, a->rd, s);
    imm = tcg_const_i32(li(s, a->li));
    rx_gen_st(a->sz, imm, ctx->src);
    tcg_temp_free_i32(imm);
    return true;
}

This will be easiest if you ever support the big-endian version of RX.

> +/* push rs */
> +static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a)
> +{
> +    if (a->rs != 0) {
> +        tcg_gen_subi_i32(cpu_regs[0], cpu_regs[0], 4);
> +        rx_gen_ldst(a->sz, RX_MEMORY_ST, cpu_regs[a->rs], cpu_regs[0]);
> +    } else {
> +        tcg_gen_mov_i32(ctx->src, cpu_regs[a->rs]);
> +        tcg_gen_subi_i32(cpu_regs[0], cpu_regs[0], 4);
> +        rx_gen_ldst(a->sz, RX_MEMORY_ST, ctx->src, cpu_regs[0]);
> +    }
> +    return true;

As far as I can see the THEN and ELSE cases have identical operation.

> +static bool trans_XCHG_rl(DisasContext *ctx, arg_XCHG_rl *a)
> +{
> +    int sz;
> +    TCGv tmp;
> +    tmp = tcg_temp_new();
> +    if (a->ld == 3) {
> +       /* xchg rs,rd */
> +        tcg_gen_mov_i32(tmp, cpu_regs[a->rs]);
> +        tcg_gen_mov_i32(cpu_regs[a->rs], cpu_regs[a->rd]);
> +        tcg_gen_mov_i32(cpu_regs[a->rd], tmp);
> +    } else {
> +        switch (a->mi) {
> +        case 0 ... 2:
> +            rx_index_addr(a->ld, a->mi, a->rs, ctx);
> +            rx_gen_ldst(a->mi, RX_MEMORY_LD, tmp, ctx->src);
> +            sz = a->mi;
> +            break;
> +        case 3:
> +            rx_index_addr(a->ld, RX_MEMORY_WORD, a->rs, ctx);
> +            rx_gen_ldu(RX_MEMORY_WORD, tmp, ctx->src);
> +            sz = RX_MEMORY_WORD;
> +            break;
> +        case 4:
> +            rx_index_addr(a->ld, RX_MEMORY_BYTE, a->rs, ctx);
> +            rx_gen_ldu(RX_MEMORY_BYTE, tmp, ctx->src);
> +            sz = RX_MEMORY_BYTE;
> +            break;
> +        }
> +        rx_gen_ldst(sz, RX_MEMORY_ST, cpu_regs[a->rd], ctx->src);
> +        tcg_gen_mov_i32(cpu_regs[a->rd], tmp);
> +    }

While I doubt that there is an SMP RX cpu, I think this should still implement
an atomic xchg operation.  This will allow rx-linux-user to run on SMP host
cpus.  Thus use

static inline TCGMemOp mi_to_mop(unsigned mi)
{
    static const TCGMemOp mop[5] = { MO_SB, MO_SW, MO_UL, MO_UH, MO_UB };
    tcg_debug_assert(mi < 5);
    return mop[mi];
}

  tcg_gen_atomic_xchg_i32(cpu_regs[a->rd], ctx->src, cpu_regs[a->rd],
                          0, mi_to_mop(a->mi));


> +#define STCOND(cond)                                                    \
> +    do {                                                                \
> +        tcg_gen_movi_i32(ctx->imm, a->imm);            \
> +        tcg_gen_movi_i32(ctx->one, 1);                                  \
> +        tcg_gen_movcond_i32(cond, cpu_regs[a->rd], cpu_psw_z,           \
> +                            ctx->one, ctx->imm, cpu_regs[a->rd]);       \
> +        return true;                                                    \
> +    } while(0)

Unused?  This seems close to what you wanted instead of...

> +#define STZFN(maskop)                                                   \
> +    do {                                                                \
> +        TCGv mask, imm;                                                 \
> +        mask = tcg_temp_new();                                          \
> +        imm = tcg_temp_new();                                           \
> +        maskop;                                                         \
> +        tcg_gen_andi_i32(imm, mask, a->imm);                            \
> +        tcg_gen_andc_i32(cpu_regs[a->rd], cpu_regs[a->rd], mask);       \
> +        tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], imm);          \
> +        tcg_temp_free(mask);                                            \
> +        tcg_temp_free(imm);                                             \
> +        return true;                                                    \
> +    } while(0)

... this.

  TCGv zero = tcg_const_i32(0);
  TCGv imm = tcg_const_i32(a->imm);
  tcg_gen_movcond(COND, cpu_regs[a->rd], cpu_psw_z, zero,
                  imm, cpu_regs[a->rd]);

where trans_STZ passes TCG_COND_NE and STNZ passes TCG_COND_EQ.

In addition, there's no point in using a #define when a function would work
just as well.

> +#define GEN_LOGIC_OP(opr, ret, arg1, arg2) \
> +    do {                                                                \
> +        opr(ret, arg1, arg2);                                       \
> +        tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_z, ret, 0);           \
> +        tcg_gen_setcondi_i32(TCG_COND_GEU, cpu_psw_s, ret, 0x80000000UL); \
> +    } while(0)

Note that the second is the same as

  tcg_gen_setcondi_i32(TCG_COND_LT, cpu_psw_s, ret, 0);
or
  tcg_gen_shri_i32(cpu_psw_s, ret, 31);

That said, this extra computation is exactly why ARM (and others) represent the
flag differently in the field.  The inline setting would be

  tcg_gen_mov_i32(cpu_psw_z, ret);
  tcg_gen_mov_i32(cpu_psw_s, ret);

And then whenever we *use* these flags, we only look at the sign bit of S, and
we see if the whole of Z is zero/non-zero.  Similarly, the overflow flag is
also stored in the sign bit of O, so that is computed more easily:

  O = (ret ^ in1) & ~(in1 & in2)

with no right-shift required at the end.

This also means that we've reduced the amount of inline computation to the
point that there is no point in *not* doing the computation every time it is
required, and so update_psw_o and cpu->psw_v[] goes away.

Again, no reason for a macro when a function will do.
They are easier to debug.

> +    rs = (a->rs2 < 16) ? a->rs2 : a->rd;

Is this due to

> @b2_rds_uimm4   .... .... imm:4 rd:4            &rri rs2=255 len=2

this?  A better decode is

@b2_rds_uimm4   .... .... imm:4 rd:4    &rri rs2=%b2_r_0 len=2

i.e. extract the rd field twice.  Once directly into rd and again from the same
bits into rs2.

> +/* revw rs, rd */
> +static bool trans_REVW(DisasContext *ctx, arg_REVW *a)
> +{
> +    TCGv hi, lo;
> +
> +    hi = tcg_temp_new();
> +    lo = tcg_temp_new();
> +    tcg_gen_shri_i32(hi, cpu_regs[a->rs], 16);
> +    tcg_gen_bswap16_i32(hi, hi);
> +    tcg_gen_shli_i32(hi, hi, 16);
> +    tcg_gen_bswap16_i32(lo, cpu_regs[a->rs]);
> +    tcg_gen_or_i32(cpu_regs[a->rd], hi, lo);
> +    tcg_temp_free(hi);
> +    tcg_temp_free(lo);
> +    return true;
> +}

The bswap16 primitive requires the input to be zero-extended.
Thus this second bswap16 may fail.  Perhaps better as

  T0 = 0x00ff00ff;
  T1 = RS & T0;
  T2 = RS >> 8;
  T1 = T1 << 8;
  T2 = T2 & T0;
  RD = T1 | T2;

> +/* conditional branch helper */
> +static void rx_bcnd_main(DisasContext *ctx, int cd, int dst, int len)
> +{
> +        TCGv t, f, cond;
> +        switch(cd) {
> +        case 0 ... 13:

Indentation is off?

> +static bool trans_BCnd_s(DisasContext *ctx, arg_BCnd_s *a)
> +{
> +    if (a->dsp < 3)
> +        a->dsp += 8;

This bit should probably be in the decode, via !function.

> +static int16_t rev16(uint16_t dsp)
> +{
> +    return ((dsp << 8) & 0xff00) | ((dsp >> 8) & 0x00ff);
> +}
> +
> +static int32_t rev24(uint32_t dsp)
> +{
> +    dsp = ((dsp << 16) & 0xff0000) |
> +        (dsp & 0x00ff00) |
> +        ((dsp >> 16) & 0x0000ff);
> +    dsp |= (dsp & 0x00800000) ? 0xff000000 : 0x00000000;
> +    return dsp;
> +}

These could also be in the decode, probably with %some_field.
But as with MOV above, maybe ought to be done via an explicit
call to li(), in order to get a (potential) big-endian RX to work.

Also, watch out for CODING_STYLE violations.
Please recheck everything with ./scripts/checkpatch.pl.

> +/* mvtachi rs */
> +static bool trans_MVTACHI(DisasContext *ctx, arg_MVTACHI *a)
> +{
> +    TCGv_i32 hi, lo;
> +    hi = tcg_temp_new_i32();
> +    lo = tcg_temp_new_i32();
> +    tcg_gen_extr_i64_i32(lo, hi, cpu_acc);
> +    tcg_gen_concat_i32_i64(cpu_acc, lo, cpu_regs[a->rs]);
> +    tcg_temp_free_i32(hi);
> +    tcg_temp_free_i32(lo);
> +    return true;
> +}

Hmm.  How about

   TCGv_i64 rs64 = tcg_temp_new_i64();
   tcg_gen_extu_i32_i64(rs64, cpu_regs[a->rs]);
   tcg_gen_deposit_i64(cpu_acc, cpu_acc, rs64, 32, 32);

> +static inline void clrsetpsw(int dst, int mode)
> +{
> +    TCGv psw[] = {
> +        cpu_psw_c, cpu_psw_z, cpu_psw_s, cpu_psw_o,
> +        NULL, NULL, NULL, NULL,
> +        cpu_psw_i, cpu_psw_u, NULL, NULL,
> +        NULL, NULL, NULL, NULL
> +    };
> +    TCGLabel *skip;
> +
> +    skip = gen_new_label();
> +    if (dst >= 8)
> +        tcg_gen_brcondi_i32(TCG_COND_NE, cpu_psw_pm, 0, skip);
> +    tcg_gen_movi_i32(psw[dst], mode);
> +    if (dst == 3)
> +        tcg_gen_movi_i32(cpu_pswop, RX_PSW_OP_NONE);
> +    gen_set_label(skip);
> +}

When clearing psw_i, you'll need to exit to the main loop so that any pending
interrupts are recognized.  Otherwise you risk going around in a loop and never
delivering the interrupt at all.

> +/* rtfi */
> +static bool trans_RTFI(DisasContext *ctx, arg_RTFI *a)
> +{
> +    check_previleged();
> +    tcg_gen_mov_i32(cpu_pc, cpu_bpc);
> +    tcg_gen_mov_i32(cpu_psw, cpu_bpsw);
> +    gen_helper_unpack_psw(cpu_env);
> +    ctx->base.is_jmp = DISAS_JUMP;
> +    return true;
> +}

Likewise, you need to exit to the main loop, because psw_i changed.

> +    cpu_pswop = tcg_global_mem_new_i32(cpu_env,
> +                                     offsetof(CPURXState, psw_op), "");
> +    for (i = 0; i < 3; i++) {
> +        cpu_pswop_v[i] = tcg_global_mem_new_i32(cpu_env,
> +                                                offsetof(CPURXState, psw_v[i]), "");

Using the empty string for the name of a temp is cruel.
That'll be very hard to debug.

> +uint32_t psw_cond(CPURXState *env, uint32_t cond)

In general this can be inlined fairly easily.
See, for instance, target/arm/translate.c, arm_test_cc.

> +uint32_t helper_div(CPURXState *env, uint32_t num, uint32_t den)
> +{
> +    uint32_t ret = num;
> +    if (den != 0) {
> +        ret = (int32_t)num / (int32_t)den;
> +    }
> +    env->psw_o = (ret == 0 || den == 0);

That is not the correct overflow condition.
ret == 0 is irrelevant, but INT_MIN / -1 is relevant.


Finally, this was way too big to review properly.  This patch set will need to
be broken up into smaller pieces.  Perhaps introducing just one, or a few
related instructions with each patch.


r~

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2019-03-08  1:24 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20190122121413.31437-1-ysato@users.sourceforge.jp>
2019-03-02  6:21 ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 01/11] target/rx: TCG Translation Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 02/11] target/rx: TCG helper Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 03/11] target/rx: CPU definition Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 04/11] target/rx: RX disassembler Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 05/11] target/rx: miscellaneous functions Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 06/11] RX62N interrupt contorol uint Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 07/11] RX62N internal timer modules Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 08/11] RX62N internal serial communication interface Yoshinori Sato
2019-03-02 19:21     ` Philippe Mathieu-Daudé
2019-03-03 13:50       ` Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 09/11] RX Target hardware definition Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 10/11] Add rx-softmmu Yoshinori Sato
2019-03-02  6:21   ` [Qemu-devel] [PATCH RFC v3 11/11] MAINTAINERS: Add RX entry Yoshinori Sato
2019-03-02 19:03     ` Philippe Mathieu-Daudé
2019-03-03 13:40       ` Yoshinori Sato
2019-03-02  6:37   ` [Qemu-devel] [PATCH RFC v3 00/11] Add RX archtecture support no-reply
2019-03-02 18:51   ` Philippe Mathieu-Daudé
2019-03-03  8:39     ` Yoshinori Sato
2019-03-08  1:24   ` Richard Henderson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.