diff options
author | Michael Rolnik <mrolnik@gmail.com> | 2020-01-24 01:51:13 +0100 |
---|---|---|
committer | Philippe Mathieu-Daudé <f4bug@amsat.org> | 2020-07-11 11:02:05 +0200 |
commit | 5718cef05a0fe306646dd0481eb6ccf4fef3979e (patch) | |
tree | 96b88602f3de54c4fd5f9b3b60e3ff74161c23fb /target/avr | |
parent | 9732b024f79217fbc685895791c4897d14096ef3 (diff) |
target/avr: Add instruction translation - Bit and Bit-test Instructions
This includes:
- LSR, ROR
- ASR
- SWAP
- SBI, CBI
- BST, BLD
- BSET, BCLR
Signed-off-by: Michael Rolnik <mrolnik@gmail.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Aleksandar Markovic <aleksandar.m.mail@gmail.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Aleksandar Markovic <aleksandar.m.mail@gmail.com>
Signed-off-by: Thomas Huth <huth@tuxfamily.org>
Message-Id: <20200705140315.260514-15-huth@tuxfamily.org>
Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Diffstat (limited to 'target/avr')
-rw-r--r-- | target/avr/insn.decode | 14 | ||||
-rw-r--r-- | target/avr/translate.c | 247 |
2 files changed, 261 insertions, 0 deletions
diff --git a/target/avr/insn.decode b/target/avr/insn.decode index 279ddfbc0c..7bb6ce7495 100644 --- a/target/avr/insn.decode +++ b/target/avr/insn.decode @@ -163,3 +163,17 @@ XCH 1001 001 rd:5 0100 LAC 1001 001 rd:5 0110 LAS 1001 001 rd:5 0101 LAT 1001 001 rd:5 0111 + +# +# Bit and Bit-test Instructions +# +LSR 1001 010 rd:5 0110 +ROR 1001 010 rd:5 0111 +ASR 1001 010 rd:5 0101 +SWAP 1001 010 rd:5 0010 +SBI 1001 1010 reg:5 bit:3 +CBI 1001 1000 reg:5 bit:3 +BST 1111 101 rd:5 0 bit:3 +BLD 1111 100 rd:5 0 bit:3 +BSET 1001 0100 0 bit:3 1000 +BCLR 1001 0100 1 bit:3 1000 diff --git a/target/avr/translate.c b/target/avr/translate.c index fce8b5af9a..884fbb6081 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -2493,3 +2493,250 @@ static bool trans_LAT(DisasContext *ctx, arg_LAT *a) return true; } + +/* + * Bit and Bit-test Instructions + */ +static void gen_rshift_ZNVSf(TCGv R) +{ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */ + tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf = R(7) */ + tcg_gen_xor_tl(cpu_Vf, cpu_Nf, cpu_Cf); + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf = Nf ^ Vf */ +} + +/* + * Shifts all bits in Rd one place to the right. Bit 7 is cleared. Bit 0 is + * loaded into the C Flag of the SREG. This operation effectively divides an + * unsigned value by two. The C Flag can be used to round the result. + */ +static bool trans_LSR(DisasContext *ctx, arg_LSR *a) +{ + TCGv Rd = cpu_r[a->rd]; + + tcg_gen_andi_tl(cpu_Cf, Rd, 1); + tcg_gen_shri_tl(Rd, Rd, 1); + + /* update status register */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, Rd, 0); /* Zf = Rd == 0 */ + tcg_gen_movi_tl(cpu_Nf, 0); + tcg_gen_mov_tl(cpu_Vf, cpu_Cf); + tcg_gen_mov_tl(cpu_Sf, cpu_Vf); + + return true; +} + +/* + * Shifts all bits in Rd one place to the right. The C Flag is shifted into + * bit 7 of Rd. Bit 0 is shifted into the C Flag. This operation, combined + * with ASR, effectively divides multi-byte signed values by two. Combined with + * LSR it effectively divides multi-byte unsigned values by two. The Carry Flag + * can be used to round the result. + */ +static bool trans_ROR(DisasContext *ctx, arg_ROR *a) +{ + TCGv Rd = cpu_r[a->rd]; + TCGv t0 = tcg_temp_new_i32(); + + tcg_gen_shli_tl(t0, cpu_Cf, 7); + + /* update status register */ + tcg_gen_andi_tl(cpu_Cf, Rd, 1); + + /* update output register */ + tcg_gen_shri_tl(Rd, Rd, 1); + tcg_gen_or_tl(Rd, Rd, t0); + + /* update status register */ + gen_rshift_ZNVSf(Rd); + + tcg_temp_free_i32(t0); + + return true; +} + +/* + * Shifts all bits in Rd one place to the right. Bit 7 is held constant. Bit 0 + * is loaded into the C Flag of the SREG. This operation effectively divides a + * signed value by two without changing its sign. The Carry Flag can be used to + * round the result. + */ +static bool trans_ASR(DisasContext *ctx, arg_ASR *a) +{ + TCGv Rd = cpu_r[a->rd]; + TCGv t0 = tcg_temp_new_i32(); + + /* update status register */ + tcg_gen_andi_tl(cpu_Cf, Rd, 1); /* Cf = Rd(0) */ + + /* update output register */ + tcg_gen_andi_tl(t0, Rd, 0x80); /* Rd = (Rd & 0x80) | (Rd >> 1) */ + tcg_gen_shri_tl(Rd, Rd, 1); + tcg_gen_or_tl(Rd, Rd, t0); + + /* update status register */ + gen_rshift_ZNVSf(Rd); + + tcg_temp_free_i32(t0); + + return true; +} + +/* + * Swaps high and low nibbles in a register. + */ +static bool trans_SWAP(DisasContext *ctx, arg_SWAP *a) +{ + TCGv Rd = cpu_r[a->rd]; + TCGv t0 = tcg_temp_new_i32(); + TCGv t1 = tcg_temp_new_i32(); + + tcg_gen_andi_tl(t0, Rd, 0x0f); + tcg_gen_shli_tl(t0, t0, 4); + tcg_gen_andi_tl(t1, Rd, 0xf0); + tcg_gen_shri_tl(t1, t1, 4); + tcg_gen_or_tl(Rd, t0, t1); + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + + return true; +} + +/* + * Sets a specified bit in an I/O Register. This instruction operates on + * the lower 32 I/O Registers -- addresses 0-31. + */ +static bool trans_SBI(DisasContext *ctx, arg_SBI *a) +{ + TCGv data = tcg_temp_new_i32(); + TCGv port = tcg_const_i32(a->reg); + + gen_helper_inb(data, cpu_env, port); + tcg_gen_ori_tl(data, data, 1 << a->bit); + gen_helper_outb(cpu_env, port, data); + + tcg_temp_free_i32(port); + tcg_temp_free_i32(data); + + return true; +} + +/* + * Clears a specified bit in an I/O Register. This instruction operates on + * the lower 32 I/O Registers -- addresses 0-31. + */ +static bool trans_CBI(DisasContext *ctx, arg_CBI *a) +{ + TCGv data = tcg_temp_new_i32(); + TCGv port = tcg_const_i32(a->reg); + + gen_helper_inb(data, cpu_env, port); + tcg_gen_andi_tl(data, data, ~(1 << a->bit)); + gen_helper_outb(cpu_env, port, data); + + tcg_temp_free_i32(data); + tcg_temp_free_i32(port); + + return true; +} + +/* + * Stores bit b from Rd to the T Flag in SREG (Status Register). + */ +static bool trans_BST(DisasContext *ctx, arg_BST *a) +{ + TCGv Rd = cpu_r[a->rd]; + + tcg_gen_andi_tl(cpu_Tf, Rd, 1 << a->bit); + tcg_gen_shri_tl(cpu_Tf, cpu_Tf, a->bit); + + return true; +} + +/* + * Copies the T Flag in the SREG (Status Register) to bit b in register Rd. + */ +static bool trans_BLD(DisasContext *ctx, arg_BLD *a) +{ + TCGv Rd = cpu_r[a->rd]; + TCGv t1 = tcg_temp_new_i32(); + + tcg_gen_andi_tl(Rd, Rd, ~(1u << a->bit)); /* clear bit */ + tcg_gen_shli_tl(t1, cpu_Tf, a->bit); /* create mask */ + tcg_gen_or_tl(Rd, Rd, t1); + + tcg_temp_free_i32(t1); + + return true; +} + +/* + * Sets a single Flag or bit in SREG. + */ +static bool trans_BSET(DisasContext *ctx, arg_BSET *a) +{ + switch (a->bit) { + case 0x00: + tcg_gen_movi_tl(cpu_Cf, 0x01); + break; + case 0x01: + tcg_gen_movi_tl(cpu_Zf, 0x01); + break; + case 0x02: + tcg_gen_movi_tl(cpu_Nf, 0x01); + break; + case 0x03: + tcg_gen_movi_tl(cpu_Vf, 0x01); + break; + case 0x04: + tcg_gen_movi_tl(cpu_Sf, 0x01); + break; + case 0x05: + tcg_gen_movi_tl(cpu_Hf, 0x01); + break; + case 0x06: + tcg_gen_movi_tl(cpu_Tf, 0x01); + break; + case 0x07: + tcg_gen_movi_tl(cpu_If, 0x01); + break; + } + + return true; +} + +/* + * Clears a single Flag in SREG. + */ +static bool trans_BCLR(DisasContext *ctx, arg_BCLR *a) +{ + switch (a->bit) { + case 0x00: + tcg_gen_movi_tl(cpu_Cf, 0x00); + break; + case 0x01: + tcg_gen_movi_tl(cpu_Zf, 0x00); + break; + case 0x02: + tcg_gen_movi_tl(cpu_Nf, 0x00); + break; + case 0x03: + tcg_gen_movi_tl(cpu_Vf, 0x00); + break; + case 0x04: + tcg_gen_movi_tl(cpu_Sf, 0x00); + break; + case 0x05: + tcg_gen_movi_tl(cpu_Hf, 0x00); + break; + case 0x06: + tcg_gen_movi_tl(cpu_Tf, 0x00); + break; + case 0x07: + tcg_gen_movi_tl(cpu_If, 0x00); + break; + } + + return true; +} |