From patchwork Sat Mar 2 06:21:28 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836489 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 101EE139A for ; Sat, 2 Mar 2019 06:28:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E483B2AADE for ; Sat, 2 Mar 2019 06:28:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D46F72D27B; Sat, 2 Mar 2019 06:28:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 170682AADE for ; Sat, 2 Mar 2019 06:28:43 +0000 (UTC) Received: from localhost ([127.0.0.1]:49149 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy8Y-0006aJ-Al for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:28:42 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49585) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy45-0002xb-T3 for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:12 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy29-0002ze-0E for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:12 -0500 Received: from mail02.asahi-net.or.jp ([202.224.55.14]:36026) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy25-0002sz-1y for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:03 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail02.asahi-net.or.jp (Postfix) with ESMTPA id 82D562ABF1; Sat, 2 Mar 2019 15:21:49 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id 5A64C24008A; Sat, 2 Mar 2019 15:21:48 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:28 +0900 Message-Id: <20190302062138.10713-2-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.14 Subject: [Qemu-devel] [PATCH RFC v3 01/11] target/rx: TCG Translation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 . + */ + +#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].,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]), + ""); + } +} From patchwork Sat Mar 2 06:21:29 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836483 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6D03B1515 for ; Sat, 2 Mar 2019 06:26:13 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5540F2AADE for ; Sat, 2 Mar 2019 06:26:13 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 45A8A2D27B; Sat, 2 Mar 2019 06:26:13 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id CF6482AADE for ; Sat, 2 Mar 2019 06:26:11 +0000 (UTC) Received: from localhost ([127.0.0.1]:49129 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy66-0004Zq-U9 for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:26:11 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49488) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy4S-0002vM-II for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:33 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy25-0002xl-5o for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:04 -0500 Received: from mail03.asahi-net.or.jp ([202.224.55.15]:37172) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy23-0002tU-4V for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:00 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail03.asahi-net.or.jp (Postfix) with ESMTPA id 86872303D7; Sat, 2 Mar 2019 15:21:50 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id 0490124008B; Sat, 2 Mar 2019 15:21:48 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:29 +0900 Message-Id: <20190302062138.10713-3-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.15 Subject: [Qemu-devel] [PATCH RFC v3 02/11] target/rx: TCG helper X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Yoshinori Sato --- 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 . + */ + +#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 . + */ + +#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); +} From patchwork Sat Mar 2 06:21:30 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836493 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F24081515 for ; Sat, 2 Mar 2019 06:30:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DD3182D2FA for ; Sat, 2 Mar 2019 06:30:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D155B2D30B; Sat, 2 Mar 2019 06:30:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id D4C1C2D2FA for ; Sat, 2 Mar 2019 06:30:39 +0000 (UTC) Received: from localhost ([127.0.0.1]:49195 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzyAQ-00081f-SZ for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:30:38 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49488) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy4I-0002vM-Sn for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:20 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy29-000306-7u for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:07 -0500 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:48770) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy27-0002t4-V5 for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:04 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id A4EB310C367; Sat, 2 Mar 2019 15:21:49 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id 5E7C024008E; Sat, 2 Mar 2019 15:21:49 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:30 +0900 Message-Id: <20190302062138.10713-4-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.13 Subject: [Qemu-devel] [PATCH RFC v3 03/11] target/rx: CPU definition X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Yoshinori Sato --- 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 . + */ + +#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 . + */ + +#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 . + */ + +#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 */ From patchwork Sat Mar 2 06:21:31 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836479 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 61525139A for ; Sat, 2 Mar 2019 06:25:22 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 44FE32D22A for ; Sat, 2 Mar 2019 06:25:22 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 349032D23E; Sat, 2 Mar 2019 06:25:22 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id EDCCD2D22A for ; Sat, 2 Mar 2019 06:25:19 +0000 (UTC) Received: from localhost ([127.0.0.1]:49120 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy5G-0003o2-AH for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:25:18 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49488) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy3x-0002vM-As for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:00 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy2C-00032q-7O for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:12 -0500 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:48764) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy2B-0002sx-5H for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:07 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id E1C1010C3AF; Sat, 2 Mar 2019 15:21:49 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id 9778A240089; Sat, 2 Mar 2019 15:21:49 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:31 +0900 Message-Id: <20190302062138.10713-5-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.13 Subject: [Qemu-devel] [PATCH RFC v3 04/11] target/rx: RX disassembler X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Yoshinori Sato --- 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 + * + * 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 . + */ + +#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].,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; +} From patchwork Sat Mar 2 06:21:32 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836501 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C2D381515 for ; Sat, 2 Mar 2019 06:35:27 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AA4F12D195 for ; Sat, 2 Mar 2019 06:35:27 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9A82B2D38F; Sat, 2 Mar 2019 06:35:27 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 02FCD2D195 for ; Sat, 2 Mar 2019 06:35:27 +0000 (UTC) Received: from localhost ([127.0.0.1]:49254 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzyF4-0002Or-D9 for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:35:26 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49725) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy4T-0003DI-Qt for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:33 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy25-0002xk-5a for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:03 -0500 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:48767) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy23-0002t3-5A for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:00 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id 1E15210C3B0; Sat, 2 Mar 2019 15:21:50 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id DAF7B24008A; Sat, 2 Mar 2019 15:21:49 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:32 +0900 Message-Id: <20190302062138.10713-6-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.13 Subject: [Qemu-devel] [PATCH RFC v3 05/11] target/rx: miscellaneous functions X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Yoshinori Sato --- 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 . + */ +#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; + } +} From patchwork Sat Mar 2 06:21:33 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836485 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CAFF6139A for ; Sat, 2 Mar 2019 06:27:07 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B66D62AADE for ; Sat, 2 Mar 2019 06:27:07 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A86D32D27B; Sat, 2 Mar 2019 06:27:07 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id DC8732AADE for ; Sat, 2 Mar 2019 06:27:06 +0000 (UTC) Received: from localhost ([127.0.0.1]:49135 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy70-0005Ix-3T for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:27:06 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49585) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy4O-0002xb-LY for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:26 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy29-00030H-Ag for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:07 -0500 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:48771) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy28-0002tT-OL for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:05 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id 5EB6410C3F0; Sat, 2 Mar 2019 15:21:50 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id 28ED0240089; Sat, 2 Mar 2019 15:21:50 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:33 +0900 Message-Id: <20190302062138.10713-7-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.13 Subject: [Qemu-devel] [PATCH RFC v3 06/11] RX62N interrupt contorol uint X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 . + */ + +#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 */ From patchwork Sat Mar 2 06:21:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836487 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 74476139A for ; Sat, 2 Mar 2019 06:28:26 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5A4E22AADE for ; Sat, 2 Mar 2019 06:28:26 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 481302D27B; Sat, 2 Mar 2019 06:28:26 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id F16FA2AADE for ; Sat, 2 Mar 2019 06:28:24 +0000 (UTC) Received: from localhost ([127.0.0.1]:49145 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy8G-0006OY-82 for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:28:24 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49488) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy4D-0002vM-6z for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:15 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy2B-00031F-1q for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:10 -0500 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:48783) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy29-0002yF-8h for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:06 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id A994410C404; Sat, 2 Mar 2019 15:21:50 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id 68376240089; Sat, 2 Mar 2019 15:21:50 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:34 +0900 Message-Id: <20190302062138.10713-8-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.13 Subject: [Qemu-devel] [PATCH RFC v3 07/11] RX62N internal timer modules X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 . + */ + +#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 . + */ + +#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 From patchwork Sat Mar 2 06:21:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836481 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6629C1515 for ; Sat, 2 Mar 2019 06:25:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 509462AADE for ; Sat, 2 Mar 2019 06:25:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4332F2D27B; Sat, 2 Mar 2019 06:25:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 4AC632AADE for ; Sat, 2 Mar 2019 06:25:43 +0000 (UTC) Received: from localhost ([127.0.0.1]:49127 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy5e-0004EI-GD for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:25:42 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49488) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy48-0002vM-OO for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:10 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy2C-00032x-8I for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:10 -0500 Received: from mail01.asahi-net.or.jp ([202.224.55.13]:48784) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy2B-0002yH-4g for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:07 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail01.asahi-net.or.jp (Postfix) with ESMTPA id E5CD410C40C; Sat, 2 Mar 2019 15:21:50 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id A6F6A24008A; Sat, 2 Mar 2019 15:21:50 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:35 +0900 Message-Id: <20190302062138.10713-9-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.13 Subject: [Qemu-devel] [PATCH RFC v3 08/11] RX62N internal serial communication interface X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 . + */ + +#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; From patchwork Sat Mar 2 06:21:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836495 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 109FE17E0 for ; Sat, 2 Mar 2019 06:32:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EDAA22D2FA for ; Sat, 2 Mar 2019 06:32:15 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DDB102D30B; Sat, 2 Mar 2019 06:32:15 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id D5A1F2D2FA for ; Sat, 2 Mar 2019 06:32:14 +0000 (UTC) Received: from localhost ([127.0.0.1]:49205 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzyBy-0000ZR-4s for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:32:14 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49488) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy4P-0002vM-NG for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:27 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy28-0002zT-Rg for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:07 -0500 Received: from mail02.asahi-net.or.jp ([202.224.55.14]:36028) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy25-0002td-5V for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:03 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail02.asahi-net.or.jp (Postfix) with ESMTPA id 30B012ACC6; Sat, 2 Mar 2019 15:21:51 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id E19B3240089; Sat, 2 Mar 2019 15:21:50 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:36 +0900 Message-Id: <20190302062138.10713-10-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.14 Subject: [Qemu-devel] [PATCH RFC v3 09/11] RX Target hardware definition X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP rx62n - RX62N cpu. rxqemu - QEMU virtual target. Signed-off-by: Yoshinori Sato --- 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 . + */ + +#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 . + */ + +#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 . + */ + +#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 From patchwork Sat Mar 2 06:21:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836491 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8F50B17E0 for ; Sat, 2 Mar 2019 06:30:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 77F322D2FA for ; Sat, 2 Mar 2019 06:30:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 659722D30B; Sat, 2 Mar 2019 06:30:19 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 097272D2FA for ; Sat, 2 Mar 2019 06:30:19 +0000 (UTC) Received: from localhost ([127.0.0.1]:49193 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzyA6-0007mN-Ax for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:30:18 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49585) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy4I-0002xb-Pu for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:19 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy2A-00030u-Ts for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:07 -0500 Received: from mail02.asahi-net.or.jp ([202.224.55.14]:36031) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy28-0002tq-Vd for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:05 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail02.asahi-net.or.jp (Postfix) with ESMTPA id 6BA7B2ACC8; Sat, 2 Mar 2019 15:21:51 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id 2D34F24008A; Sat, 2 Mar 2019 15:21:51 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:37 +0900 Message-Id: <20190302062138.10713-11-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.14 Subject: [Qemu-devel] [PATCH RFC v3 10/11] Add rx-softmmu X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Yoshinori Sato --- 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; From patchwork Sat Mar 2 06:21:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshinori Sato X-Patchwork-Id: 10836499 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D5E2117E0 for ; Sat, 2 Mar 2019 06:34:09 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C012328994 for ; Sat, 2 Mar 2019 06:34:09 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id ADA00289AB; Sat, 2 Mar 2019 06:34:09 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 48CFE28994 for ; Sat, 2 Mar 2019 06:34:09 +0000 (UTC) Received: from localhost ([127.0.0.1]:49218 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzyDo-0001fA-Lr for patchwork-qemu-devel@patchwork.kernel.org; Sat, 02 Mar 2019 01:34:08 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49585) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy4R-0002xb-U8 for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:24:28 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzy28-0002zM-RX for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:06 -0500 Received: from mail02.asahi-net.or.jp ([202.224.55.14]:36034) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzy25-0002u1-5B for qemu-devel@nongnu.org; Sat, 02 Mar 2019 01:22:03 -0500 Received: from h61-195-96-97.vps.ablenet.jp (h61-195-96-97.vps.ablenet.jp [61.195.96.97]) (Authenticated sender: PQ4Y-STU) by mail02.asahi-net.or.jp (Postfix) with ESMTPA id A081F2ACD2; Sat, 2 Mar 2019 15:21:51 +0900 (JST) Received: from ysato.dip.jp (ZM005235.ppp.dion.ne.jp [222.8.5.235]) by h61-195-96-97.vps.ablenet.jp (Postfix) with ESMTPSA id 68E13240089; Sat, 2 Mar 2019 15:21:51 +0900 (JST) From: Yoshinori Sato To: qemu-devel@nongnu.org Date: Sat, 2 Mar 2019 15:21:38 +0900 Message-Id: <20190302062138.10713-12-ysato@users.sourceforge.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190302062138.10713-1-ysato@users.sourceforge.jp> References: <20190122121413.31437-1-ysato@users.sourceforge.jp> <20190302062138.10713-1-ysato@users.sourceforge.jp> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 202.224.55.14 Subject: [Qemu-devel] [PATCH RFC v3 11/11] MAINTAINERS: Add RX entry. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, richard.henderson@linaro.org, Yoshinori Sato Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Yoshinori Sato --- 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 +S: Maintained +F: target/rx/ +F: hw/rx/ +F: include/hw/rx/ +F: disas/rx.c + S390 M: Richard Henderson M: David Hildenbrand @@ -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 +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