diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2004-12-19 23:18:01 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2004-12-19 23:18:01 +0000 |
commit | e80cfcfc8884400e826328b772971913a14d0f44 (patch) | |
tree | f9bbe461ff5dd6f13fbdbd781053309564c0b872 /target-sparc | |
parent | 9772c73bbc9f24a66bf060e16c0625a258b1bb41 (diff) | |
download | qemu-e80cfcfc8884400e826328b772971913a14d0f44.zip qemu-e80cfcfc8884400e826328b772971913a14d0f44.tar.gz qemu-e80cfcfc8884400e826328b772971913a14d0f44.tar.bz2 |
SPARC merge
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1179 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'target-sparc')
-rw-r--r-- | target-sparc/cpu.h | 28 | ||||
-rw-r--r-- | target-sparc/exec.h | 3 | ||||
-rw-r--r-- | target-sparc/fop_template.h | 12 | ||||
-rw-r--r-- | target-sparc/helper.c | 255 | ||||
-rw-r--r-- | target-sparc/op.c | 29 | ||||
-rw-r--r-- | target-sparc/op_helper.c | 176 | ||||
-rw-r--r-- | target-sparc/op_mem.h | 4 | ||||
-rw-r--r-- | target-sparc/translate.c | 396 |
8 files changed, 667 insertions, 236 deletions
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index adf8df2..a66d2e3 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -10,23 +10,42 @@ /* trap definitions */ #define TT_ILL_INSN 0x02 #define TT_PRIV_INSN 0x03 +#define TT_NFPU_INSN 0x04 #define TT_WIN_OVF 0x05 #define TT_WIN_UNF 0x06 #define TT_FP_EXCP 0x08 #define TT_DIV_ZERO 0x2a #define TT_TRAP 0x80 +#define TT_EXTINT 0x10 #define PSR_NEG (1<<23) #define PSR_ZERO (1<<22) #define PSR_OVF (1<<21) #define PSR_CARRY (1<<20) #define PSR_ICC (PSR_NEG|PSR_ZERO|PSR_OVF|PSR_CARRY) +#define PSR_EF (1<<12) +#define PSR_PIL 0xf00 #define PSR_S (1<<7) #define PSR_PS (1<<6) #define PSR_ET (1<<5) #define PSR_CWP 0x1f /* Fake impl 0, version 4 */ -#define GET_PSR(env) ((0<<28) | (4<<24) | env->psr | (env->psrs? PSR_S : 0) | (env->psrs? PSR_PS : 0) |(env->psret? PSR_ET : 0) | env->cwp) +#define GET_PSR(env) ((0 << 28) | (4 << 24) | env->psr | \ + (env->psref? PSR_EF : 0) | \ + (env->psrpil << 8) | \ + (env->psrs? PSR_S : 0) | \ + (env->psrs? PSR_PS : 0) | \ + (env->psret? PSR_ET : 0) | env->cwp) + +#define PUT_PSR(env, val) do { int _tmp = val; \ + env->psr = _tmp & ~PSR_ICC; \ + env->psref = (_tmp & PSR_EF)? 1 : 0; \ + env->psrpil = (_tmp & PSR_PIL) >> 8; \ + env->psrs = (_tmp & PSR_S)? 1 : 0; \ + env->psrps = (_tmp & PSR_PS)? 1 : 0; \ + env->psret = (_tmp & PSR_ET)? 1 : 0; \ + set_cwp(_tmp & PSR_CWP & (NWINDOWS - 1)); \ + } while (0) /* Trap base register */ #define TBR_BASE_MASK 0xfffff000 @@ -65,6 +84,9 @@ #define FSR_FTT1 (1<<15) #define FSR_FTT0 (1<<14) #define FSR_FTT_MASK (FSR_FTT2 | FSR_FTT1 | FSR_FTT0) +#define FSR_FTT_IEEE_EXCP (1 << 14) +#define FSR_FTT_UNIMPFPOP (3 << 14) +#define FSR_FTT_INVAL_FPR (6 << 14) #define FSR_FCC1 (1<<11) #define FSR_FCC0 (1<<10) @@ -106,6 +128,8 @@ typedef struct CPUSPARCState { int psrs; /* supervisor mode (extracted from PSR) */ int psrps; /* previous supervisor mode */ int psret; /* enable traps */ + int psrpil; /* interrupt level */ + int psref; /* enable fpu */ jmp_buf jmp_env; int user_mode_only; int exception_index; @@ -144,6 +168,8 @@ typedef struct CPUSPARCState { CPUSPARCState *cpu_sparc_init(void); int cpu_sparc_exec(CPUSPARCState *s); int cpu_sparc_close(CPUSPARCState *s); +void cpu_get_fp64(uint64_t *pmant, uint16_t *pexp, double f); +double cpu_put_fp64(uint64_t mant, uint16_t exp); struct siginfo; int cpu_sparc_signal_handler(int hostsignum, struct siginfo *info, void *puc); diff --git a/target-sparc/exec.h b/target-sparc/exec.h index 1b3d9a0..f8edc17 100644 --- a/target-sparc/exec.h +++ b/target-sparc/exec.h @@ -40,6 +40,9 @@ void do_interrupt(int intno, int is_int, int error_code, void raise_exception_err(int exception_index, int error_code); void raise_exception(int tt); void memcpy32(uint32_t *dst, const uint32_t *src); +uint32_t mmu_probe(uint32_t address, int mmulev); +void dump_mmu(void); +void helper_debug(); /* XXX: move that to a generic header */ #if !defined(CONFIG_USER_ONLY) diff --git a/target-sparc/fop_template.h b/target-sparc/fop_template.h index 2987b68..eb1e1e3 100644 --- a/target-sparc/fop_template.h +++ b/target-sparc/fop_template.h @@ -51,18 +51,6 @@ void OPPROTO glue(op_store_FT2_fpr_fpr, REGNAME)(void) } /* double floating point registers moves */ -#if 0 -#define CPU_DOUBLE_U_DEF -typedef union { - double d; - struct { - uint32_t lower; - uint32_t upper; - } l; - uint64_t ll; -} CPU_DoubleU; -#endif /* CPU_DOUBLE_U_DEF */ - void OPPROTO glue(op_load_fpr_DT0_fpr, REGNAME)(void) { CPU_DoubleU u; diff --git a/target-sparc/helper.c b/target-sparc/helper.c index 93ef930..76ad643 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -19,7 +19,8 @@ */ #include "exec.h" -#define DEBUG_PCALL +//#define DEBUG_PCALL +//#define DEBUG_MMU /* Sparc MMU emulation */ int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, @@ -108,80 +109,71 @@ static const int rw_table[2][8] = { { 0, 1, 0, 1, 0, 0, 0, 0 } }; - -/* Perform address translation */ -int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, - int is_user, int is_softmmu) +int get_physical_address (CPUState *env, uint32_t *physical, int *prot, + int *access_index, uint32_t address, int rw, + int is_user) { - int exception = 0; - int access_perms = 0, access_index = 0; - uint8_t *pde_ptr; + int access_perms = 0; + target_phys_addr_t pde_ptr; uint32_t pde, virt_addr; - int error_code = 0, is_dirty, prot, ret = 0; - unsigned long paddr, vaddr, page_offset; - - if (env->user_mode_only) { - /* user mode only emulation */ - ret = -2; - goto do_fault; - } + int error_code = 0, is_dirty; + unsigned long page_offset; virt_addr = address & TARGET_PAGE_MASK; if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ - paddr = address; - page_offset = address & (TARGET_PAGE_SIZE - 1); - prot = PAGE_READ | PAGE_WRITE; - goto do_mapping; + *physical = address; + *prot = PAGE_READ | PAGE_WRITE; + return 0; } /* SPARC reference MMU table walk: Context table->L1->L2->PTE */ /* Context base + context number */ - pde_ptr = phys_ram_base + (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4); - pde = ldl_raw(pde_ptr); + pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4); + cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); + bswap32s(&pde); /* Ctx pde */ switch (pde & PTE_ENTRYTYPE_MASK) { + default: case 0: /* Invalid */ - error_code = 1; - goto do_fault; - case 2: /* PTE, maybe should not happen? */ + return 1; + case 2: /* L0 PTE, maybe should not happen? */ case 3: /* Reserved */ - error_code = 4; - goto do_fault; - case 1: /* L1 PDE */ - pde_ptr = phys_ram_base + ((address >> 22) & ~3) + ((pde & ~3) << 4); - pde = ldl_raw(pde_ptr); + return 4; + case 1: /* L0 PDE */ + pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4); + cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); + bswap32s(&pde); switch (pde & PTE_ENTRYTYPE_MASK) { + default: case 0: /* Invalid */ - error_code = 1; - goto do_fault; + return 1; case 3: /* Reserved */ - error_code = 4; - goto do_fault; - case 1: /* L2 PDE */ - pde_ptr = phys_ram_base + ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); - pde = ldl_raw(pde_ptr); + return 4; + case 1: /* L1 PDE */ + pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); + cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); + bswap32s(&pde); switch (pde & PTE_ENTRYTYPE_MASK) { + default: case 0: /* Invalid */ - error_code = 1; - goto do_fault; + return 1; case 3: /* Reserved */ - error_code = 4; - goto do_fault; - case 1: /* L3 PDE */ - pde_ptr = phys_ram_base + ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); - pde = ldl_raw(pde_ptr); + return 4; + case 1: /* L2 PDE */ + pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); + cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); + bswap32s(&pde); switch (pde & PTE_ENTRYTYPE_MASK) { + default: case 0: /* Invalid */ - error_code = 1; - goto do_fault; + return 1; case 1: /* PDE, should not happen */ case 3: /* Reserved */ - error_code = 4; - goto do_fault; + return 4; case 2: /* L3 PTE */ virt_addr = address & TARGET_PAGE_MASK; page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1); @@ -201,40 +193,58 @@ int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, /* update page modified and dirty bits */ is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK); if (!(pde & PG_ACCESSED_MASK) || is_dirty) { + uint32_t tmppde; pde |= PG_ACCESSED_MASK; if (is_dirty) pde |= PG_MODIFIED_MASK; - stl_raw(pde_ptr, pde); + tmppde = bswap32(pde); + cpu_physical_memory_write(pde_ptr, (uint8_t *)&tmppde, 4); } - /* check access */ - access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1); + *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1); access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT; - error_code = access_table[access_index][access_perms]; + error_code = access_table[*access_index][access_perms]; if (error_code) - goto do_fault; + return error_code; /* the page can be put in the TLB */ - prot = PAGE_READ; + *prot = PAGE_READ; if (pde & PG_MODIFIED_MASK) { /* only set write access if already dirty... otherwise wait for dirty access */ if (rw_table[is_user][access_perms]) - prot |= PAGE_WRITE; + *prot |= PAGE_WRITE; } /* Even if large ptes, we map only one 4KB page in the cache to avoid filling it too fast */ - virt_addr = address & TARGET_PAGE_MASK; - paddr = ((pde & PTE_ADDR_MASK) << 4) + page_offset; + *physical = ((pde & PTE_ADDR_MASK) << 4) + page_offset; + return 0; +} + +/* Perform address translation */ +int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, + int is_user, int is_softmmu) +{ + int exception = 0; + uint32_t virt_addr, paddr; + unsigned long vaddr; + int error_code = 0, prot, ret = 0, access_index; - do_mapping: - vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1)); + if (env->user_mode_only) { + /* user mode only emulation */ + error_code = -2; + goto do_fault_user; + } - ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu); - return ret; + error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user); + if (error_code == 0) { + virt_addr = address & TARGET_PAGE_MASK; + vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1)); + ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu); + return ret; + } - do_fault: if (env->mmuregs[3]) /* Fault status register */ env->mmuregs[3] = 1; /* overflow (not read before another fault) */ env->mmuregs[3] |= (access_index << 5) | (error_code << 2) | 2; @@ -242,7 +252,7 @@ int cpu_sparc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, if (env->mmuregs[0] & MMU_NF || env->psret == 0) // No fault return 0; - + do_fault_user: env->exception_index = exception; env->error_code = error_code; return error_code; @@ -289,13 +299,14 @@ void do_interrupt(int intno, int is_int, int error_code, count, intno, error_code, is_int, env->pc, env->npc, env->regwptr[6]); -#if 0 +#if 1 cpu_dump_state(env, logfile, fprintf, 0); { int i; uint8_t *ptr; + fprintf(logfile, " code="); - ptr = env->pc; + ptr = (uint8_t *)env->pc; for(i = 0; i < 16; i++) { fprintf(logfile, " %02x", ldub(ptr + i)); } @@ -305,11 +316,18 @@ void do_interrupt(int intno, int is_int, int error_code, count++; } #endif +#if !defined(CONFIG_USER_ONLY) + if (env->psret == 0) { + fprintf(logfile, "Trap while interrupts disabled, Error state!\n"); + qemu_system_shutdown_request(); + return; + } +#endif env->psret = 0; cwp = (env->cwp - 1) & (NWINDOWS - 1); set_cwp(cwp); - env->regwptr[9] = env->pc; - env->regwptr[10] = env->npc; + env->regwptr[9] = env->pc - 4; // XXX? + env->regwptr[10] = env->pc; env->psrps = env->psrs; env->psrs = 1; env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); @@ -322,3 +340,106 @@ void raise_exception_err(int exception_index, int error_code) { raise_exception(exception_index); } + +uint32_t mmu_probe(uint32_t address, int mmulev) +{ + target_phys_addr_t pde_ptr; + uint32_t pde; + + /* Context base + context number */ + pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4); + cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); + bswap32s(&pde); + switch (pde & PTE_ENTRYTYPE_MASK) { + default: + case 0: /* Invalid */ + case 2: /* PTE, maybe should not happen? */ + case 3: /* Reserved */ + return 0; + case 1: /* L1 PDE */ + if (mmulev == 3) + return pde; + pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4); + cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); + bswap32s(&pde); + + switch (pde & PTE_ENTRYTYPE_MASK) { + default: + case 0: /* Invalid */ + case 3: /* Reserved */ + return 0; + case 2: /* L1 PTE */ + return pde; + case 1: /* L2 PDE */ + if (mmulev == 2) + return pde; + pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); + cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); + bswap32s(&pde); + + switch (pde & PTE_ENTRYTYPE_MASK) { + default: + case 0: /* Invalid */ + case 3: /* Reserved */ + return 0; + case 2: /* L2 PTE */ + return pde; + case 1: /* L3 PDE */ + if (mmulev == 1) + return pde; + pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); + cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); + bswap32s(&pde); + + switch (pde & PTE_ENTRYTYPE_MASK) { + default: + case 0: /* Invalid */ + case 1: /* PDE, should not happen */ + case 3: /* Reserved */ + return 0; + case 2: /* L3 PTE */ + return pde; + } + } + } + } + return 0; +} + +void dump_mmu(void) +{ +#ifdef DEBUG_MMU + uint32_t pa, va, va1, va2; + int n, m, o; + target_phys_addr_t pde_ptr; + uint32_t pde; + + printf("MMU dump:\n"); + pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 4); + cpu_physical_memory_read(pde_ptr, (uint8_t *)&pde, 4); + bswap32s(&pde); + printf("Root ptr: 0x%08x, ctx: %d\n", env->mmuregs[1] << 4, env->mmuregs[2]); + for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) { + pde_ptr = mmu_probe(va, 2); + if (pde_ptr) { + pa = cpu_get_phys_page_debug(env, va); + printf("VA: 0x%08x, PA: 0x%08x PDE: 0x%08x\n", va, pa, pde_ptr); + for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) { + pde_ptr = mmu_probe(va1, 1); + if (pde_ptr) { + pa = cpu_get_phys_page_debug(env, va1); + printf(" VA: 0x%08x, PA: 0x%08x PDE: 0x%08x\n", va1, pa, pde_ptr); + for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) { + pde_ptr = mmu_probe(va2, 0); + if (pde_ptr) { + pa = cpu_get_phys_page_debug(env, va2); + printf(" VA: 0x%08x, PA: 0x%08x PTE: 0x%08x\n", va2, pa, pde_ptr); + } + } + } + } + } + } + printf("MMU dump ends\n"); +#endif +} diff --git a/target-sparc/op.c b/target-sparc/op.c index 2cf4ed8..f8cf2f8 100644 --- a/target-sparc/op.c +++ b/target-sparc/op.c @@ -524,13 +524,7 @@ void OPPROTO op_rdpsr(void) void OPPROTO op_wrpsr(void) { - int cwp; - env->psr = T0 & ~PSR_ICC; - env->psrs = (T0 & PSR_S)? 1 : 0; - env->psrps = (T0 & PSR_PS)? 1 : 0; - env->psret = (T0 & PSR_ET)? 1 : 0; - cwp = (T0 & PSR_CWP) & (NWINDOWS - 1); - set_cwp(cwp); + PUT_PSR(env,T0); FORCE_RET(); } @@ -602,10 +596,27 @@ void OPPROTO op_trapcc_T0(void) FORCE_RET(); } -void OPPROTO op_debug(void) +void OPPROTO op_trap_ifnofpu(void) +{ + if (!env->psref) { + env->exception_index = TT_NFPU_INSN; + cpu_loop_exit(); + } + FORCE_RET(); +} + +void OPPROTO op_fpexception_im(void) { - env->exception_index = EXCP_DEBUG; + env->exception_index = TT_FP_EXCP; + env->fsr &= ~FSR_FTT_MASK; + env->fsr |= PARAM1; cpu_loop_exit(); + FORCE_RET(); +} + +void OPPROTO op_debug(void) +{ + helper_debug(); } void OPPROTO op_exit_tb(void) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index 3a6de7c..6dead66 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -2,6 +2,8 @@ #include <fenv.h> #include "exec.h" +//#define DEBUG_MMU + #ifdef USE_INT_TO_FLOAT_HELPERS void do_fitos(void) { @@ -33,6 +35,13 @@ void do_fcmps (void) { if (isnan(FT0) || isnan(FT1)) { T0 = FSR_FCC1 | FSR_FCC0; + env->fsr &= ~(FSR_FCC1 | FSR_FCC0); + env->fsr |= T0; + if (env->fsr & FSR_NVM) { + raise_exception(TT_FP_EXCP); + } else { + env->fsr |= FSR_NVA; + } } else if (FT0 < FT1) { T0 = FSR_FCC0; } else if (FT0 > FT1) { @@ -47,6 +56,13 @@ void do_fcmpd (void) { if (isnan(DT0) || isnan(DT1)) { T0 = FSR_FCC1 | FSR_FCC0; + env->fsr &= ~(FSR_FCC1 | FSR_FCC0); + env->fsr |= T0; + if (env->fsr & FSR_NVM) { + raise_exception(TT_FP_EXCP); + } else { + env->fsr |= FSR_NVA; + } } else if (DT0 < DT1) { T0 = FSR_FCC0; } else if (DT0 > DT1) { @@ -59,55 +75,131 @@ void do_fcmpd (void) void helper_ld_asi(int asi, int size, int sign) { - switch(asi) { + uint32_t ret; + + switch (asi) { case 3: /* MMU probe */ - T1 = 0; - return; + { + int mmulev; + + mmulev = (T0 >> 8) & 15; + if (mmulev > 4) + ret = 0; + else { + ret = mmu_probe(T0, mmulev); + //bswap32s(&ret); + } +#ifdef DEBUG_MMU + printf("mmu_probe: 0x%08x (lev %d) -> 0x%08x\n", T0, mmulev, ret); +#endif + } + break; case 4: /* read MMU regs */ { - int temp, reg = (T0 >> 8) & 0xf; + int reg = (T0 >> 8) & 0xf; - temp = env->mmuregs[reg]; + ret = env->mmuregs[reg]; if (reg == 3 || reg == 4) /* Fault status, addr cleared on read*/ - env->mmuregs[reg] = 0; - T1 = temp; + env->mmuregs[4] = 0; } - return; + break; case 0x20 ... 0x2f: /* MMU passthrough */ - { - int temp; - - cpu_physical_memory_read(T0, (void *) &temp, size); - bswap32s(&temp); - T1 = temp; - } - return; + cpu_physical_memory_read(T0, (void *) &ret, size); + if (size == 4) + bswap32s(&ret); + else if (size == 2) + bswap16s(&ret); + break; default: - T1 = 0; - return; + ret = 0; + break; } + T1 = ret; } void helper_st_asi(int asi, int size, int sign) { switch(asi) { case 3: /* MMU flush */ - return; + { + int mmulev; + + mmulev = (T0 >> 8) & 15; + switch (mmulev) { + case 0: // flush page + tlb_flush_page(cpu_single_env, T0 & 0xfffff000); + break; + case 1: // flush segment (256k) + case 2: // flush region (16M) + case 3: // flush context (4G) + case 4: // flush entire + tlb_flush(cpu_single_env, 1); + break; + default: + break; + } + dump_mmu(); + return; + } case 4: /* write MMU regs */ { - int reg = (T0 >> 8) & 0xf; + int reg = (T0 >> 8) & 0xf, oldreg; + + oldreg = env->mmuregs[reg]; if (reg == 0) { env->mmuregs[reg] &= ~(MMU_E | MMU_NF); env->mmuregs[reg] |= T1 & (MMU_E | MMU_NF); } else env->mmuregs[reg] = T1; + if (oldreg != env->mmuregs[reg]) { +#if 0 + // XXX: Only if MMU mapping change, we may need to flush? + tlb_flush(cpu_single_env, 1); + cpu_loop_exit(); + FORCE_RET(); +#endif + } + dump_mmu(); return; } + case 0x17: /* Block copy, sta access */ + { + // value (T1) = src + // address (T0) = dst + // copy 32 bytes + int src = T1, dst = T0; + uint8_t temp[32]; + + bswap32s(&src); + + cpu_physical_memory_read(src, (void *) &temp, 32); + cpu_physical_memory_write(dst, (void *) &temp, 32); + } + return; + case 0x1f: /* Block fill, stda access */ + { + // value (T1, T2) + // address (T0) = dst + // fill 32 bytes + int i, dst = T0; + uint64_t val; + + val = (((uint64_t)T1) << 32) | T2; + bswap64s(&val); + + for (i = 0; i < 32; i += 8, dst += 8) { + cpu_physical_memory_write(dst, (void *) &val, 8); + } + } + return; case 0x20 ... 0x2f: /* MMU passthrough */ { int temp = T1; - - bswap32s(&temp); + if (size == 4) + bswap32s(&temp); + else if (size == 2) + bswap16s(&temp); + cpu_physical_memory_write(T0, (void *) &temp, size); } return; @@ -116,27 +208,6 @@ void helper_st_asi(int asi, int size, int sign) } } -#if 0 -void do_ldd_raw(uint32_t addr) -{ - T1 = ldl_raw((void *) addr); - T0 = ldl_raw((void *) (addr + 4)); -} - -#if !defined(CONFIG_USER_ONLY) -void do_ldd_user(uint32_t addr) -{ - T1 = ldl_user((void *) addr); - T0 = ldl_user((void *) (addr + 4)); -} -void do_ldd_kernel(uint32_t addr) -{ - T1 = ldl_kernel((void *) addr); - T0 = ldl_kernel((void *) (addr + 4)); -} -#endif -#endif - void helper_rett() { int cwp; @@ -166,3 +237,22 @@ void helper_ldfsr(void) break; } } + +void cpu_get_fp64(uint64_t *pmant, uint16_t *pexp, double f) +{ + int exptemp; + + *pmant = ldexp(frexp(f, &exptemp), 53); + *pexp = exptemp; +} + +double cpu_put_fp64(uint64_t mant, uint16_t exp) +{ + return ldexp((double) mant, exp - 53); +} + +void helper_debug() +{ + env->exception_index = EXCP_DEBUG; + cpu_loop_exit(); +} diff --git a/target-sparc/op_mem.h b/target-sparc/op_mem.h index 2ae74f2..9c839a0 100644 --- a/target-sparc/op_mem.h +++ b/target-sparc/op_mem.h @@ -43,12 +43,8 @@ void OPPROTO glue(op_swap, MEMSUFFIX)(void) void OPPROTO glue(op_ldd, MEMSUFFIX)(void) { -#if 1 T1 = glue(ldl, MEMSUFFIX)((void *) T0); T0 = glue(ldl, MEMSUFFIX)((void *) (T0 + 4)); -#else - glue(do_ldd, MEMSUFFIX)(T0); -#endif } /*** Floating-point store ***/ diff --git a/target-sparc/translate.c b/target-sparc/translate.c index 2440c0d..2f06795 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -646,6 +646,7 @@ static void disas_sparc_insn(DisasContext * dc) switch (xop) { case 0x0: case 0x1: /* UNIMPL */ + case 0x5: /*CBN+x */ default: goto illegal_insn; case 0x2: /* BN+x */ @@ -657,16 +658,24 @@ static void disas_sparc_insn(DisasContext * dc) } case 0x6: /* FBN+x */ { +#if !defined(CONFIG_USER_ONLY) + gen_op_trap_ifnofpu(); +#endif target <<= 2; target = sign_extend(target, 22); do_fbranch(dc, target, insn); goto jmp_insn; } case 0x4: /* SETHI */ - gen_movl_imm_T0(target << 10); - gen_movl_T0_reg(rd); - break; - case 0x5: /*CBN+x */ +#define OPTIM +#if defined(OPTIM) + if (rd) { // nop +#endif + gen_movl_imm_T0(target << 10); + gen_movl_T0_reg(rd); +#if defined(OPTIM) + } +#endif break; } break; @@ -691,14 +700,24 @@ static void disas_sparc_insn(DisasContext * dc) gen_movl_reg_T0(rs1); if (IS_IMM) { rs2 = GET_FIELD(insn, 25, 31); +#if defined(OPTIM) if (rs2 != 0) { - gen_movl_imm_T1(rs2); - gen_op_add_T1_T0(); +#endif + gen_movl_imm_T1(rs2); + gen_op_add_T1_T0(); +#if defined(OPTIM) } +#endif } else { rs2 = GET_FIELD(insn, 27, 31); - gen_movl_reg_T1(rs2); - gen_op_add_T1_T0(); +#if defined(OPTIM) + if (rs2 != 0) { +#endif + gen_movl_reg_T1(rs2); + gen_op_add_T1_T0(); +#if defined(OPTIM) + } +#endif } save_state(dc); cond = GET_FIELD(insn, 3, 6); @@ -707,6 +726,7 @@ static void disas_sparc_insn(DisasContext * dc) dc->is_br = 1; goto jmp_insn; } else { + gen_cond(cond); gen_op_trapcc_T0(); } } else if (xop == 0x28) { @@ -741,7 +761,10 @@ static void disas_sparc_insn(DisasContext * dc) gen_movl_T0_reg(rd); break; #endif - } else if (xop == 0x34 || xop == 0x35) { /* FPU Operations */ + } else if (xop == 0x34) { /* FPU Operations */ +#if !defined(CONFIG_USER_ONLY) + gen_op_trap_ifnofpu(); +#endif rs1 = GET_FIELD(insn, 13, 17); rs2 = GET_FIELD(insn, 27, 31); xop = GET_FIELD(insn, 18, 26); @@ -770,6 +793,8 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_fsqrtd(); gen_op_store_DT0_fpr(rd); break; + case 0x2b: /* fsqrtq */ + goto nfpu_insn; case 0x41: gen_op_load_fpr_FT0(rs1); gen_op_load_fpr_FT1(rs2); @@ -782,6 +807,8 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_faddd(); gen_op_store_DT0_fpr(rd); break; + case 0x43: /* faddq */ + goto nfpu_insn; case 0x45: gen_op_load_fpr_FT0(rs1); gen_op_load_fpr_FT1(rs2); @@ -794,6 +821,8 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_fsubd(); gen_op_store_DT0_fpr(rd); break; + case 0x47: /* fsubq */ + goto nfpu_insn; case 0x49: gen_op_load_fpr_FT0(rs1); gen_op_load_fpr_FT1(rs2); @@ -806,6 +835,8 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_fmuld(); gen_op_store_DT0_fpr(rd); break; + case 0x4b: /* fmulq */ + goto nfpu_insn; case 0x4d: gen_op_load_fpr_FT0(rs1); gen_op_load_fpr_FT1(rs2); @@ -818,32 +849,16 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_fdivd(); gen_op_store_DT0_fpr(rd); break; - case 0x51: - gen_op_load_fpr_FT0(rs1); - gen_op_load_fpr_FT1(rs2); - gen_op_fcmps(); - break; - case 0x52: - gen_op_load_fpr_DT0(rs1); - gen_op_load_fpr_DT1(rs2); - gen_op_fcmpd(); - break; - case 0x55: /* fcmpes */ - gen_op_load_fpr_FT0(rs1); - gen_op_load_fpr_FT1(rs2); - gen_op_fcmps(); /* XXX */ - break; - case 0x56: /* fcmped */ - gen_op_load_fpr_DT0(rs1); - gen_op_load_fpr_DT1(rs2); - gen_op_fcmpd(); /* XXX */ - break; + case 0x4f: /* fdivq */ + goto nfpu_insn; case 0x69: gen_op_load_fpr_FT0(rs1); gen_op_load_fpr_FT1(rs2); gen_op_fsmuld(); gen_op_store_DT0_fpr(rd); break; + case 0x6e: /* fdmulq */ + goto nfpu_insn; case 0xc4: gen_op_load_fpr_FT1(rs2); gen_op_fitos(); @@ -854,6 +869,8 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_fdtos(); gen_op_store_FT0_fpr(rd); break; + case 0xc7: /* fqtos */ + goto nfpu_insn; case 0xc8: gen_op_load_fpr_FT1(rs2); gen_op_fitod(); @@ -864,6 +881,14 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_fstod(); gen_op_store_DT0_fpr(rd); break; + case 0xcb: /* fqtod */ + goto nfpu_insn; + case 0xcc: /* fitoq */ + goto nfpu_insn; + case 0xcd: /* fstoq */ + goto nfpu_insn; + case 0xce: /* fdtoq */ + goto nfpu_insn; case 0xd1: gen_op_load_fpr_FT1(rs2); gen_op_fstoi(); @@ -874,13 +899,85 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_fdtoi(); gen_op_store_FT0_fpr(rd); break; + case 0xd3: /* fqtoi */ + goto nfpu_insn; default: goto illegal_insn; } - } else { + } else if (xop == 0x35) { /* FPU Operations */ +#if !defined(CONFIG_USER_ONLY) + gen_op_trap_ifnofpu(); +#endif rs1 = GET_FIELD(insn, 13, 17); - gen_movl_reg_T0(rs1); - if (IS_IMM) { /* immediate */ + rs2 = GET_FIELD(insn, 27, 31); + xop = GET_FIELD(insn, 18, 26); + switch (xop) { + case 0x51: + gen_op_load_fpr_FT0(rs1); + gen_op_load_fpr_FT1(rs2); + gen_op_fcmps(); + break; + case 0x52: + gen_op_load_fpr_DT0(rs1); + gen_op_load_fpr_DT1(rs2); + gen_op_fcmpd(); + break; + case 0x53: /* fcmpq */ + goto nfpu_insn; + case 0x55: /* fcmpes */ + gen_op_load_fpr_FT0(rs1); + gen_op_load_fpr_FT1(rs2); + gen_op_fcmps(); /* XXX should trap if qNaN or sNaN */ + break; + case 0x56: /* fcmped */ + gen_op_load_fpr_DT0(rs1); + gen_op_load_fpr_DT1(rs2); + gen_op_fcmpd(); /* XXX should trap if qNaN or sNaN */ + break; + case 0x57: /* fcmpeq */ + goto nfpu_insn; + default: + goto illegal_insn; + } +#if defined(OPTIM) + } else if (xop == 0x2) { + // clr/mov shortcut + + rs1 = GET_FIELD(insn, 13, 17); + if (rs1 == 0) { + // or %g0, x, y -> mov T1, x; mov y, T1 + if (IS_IMM) { /* immediate */ + rs2 = GET_FIELDs(insn, 19, 31); + gen_movl_imm_T1(rs2); + } else { /* register */ + rs2 = GET_FIELD(insn, 27, 31); + gen_movl_reg_T1(rs2); + } + gen_movl_T1_reg(rd); + } else { + gen_movl_reg_T0(rs1); + if (IS_IMM) { /* immediate */ + // or x, #0, y -> mov T1, x; mov y, T1 + rs2 = GET_FIELDs(insn, 19, 31); + if (rs2 != 0) { + gen_movl_imm_T1(rs2); + gen_op_or_T1_T0(); + } + } else { /* register */ + // or x, %g0, y -> mov T1, x; mov y, T1 + rs2 = GET_FIELD(insn, 27, 31); + if (rs2 != 0) { + gen_movl_reg_T1(rs2); + gen_op_or_T1_T0(); + } + } + gen_movl_T0_reg(rd); + } +#endif + } else if (xop < 0x38) { + rs1 = GET_FIELD(insn, 13, 17); + gen_movl_reg_T0(rs1); + if (IS_IMM) { /* immediate */ rs2 = GET_FIELDs(insn, 19, 31); gen_movl_imm_T1(rs2); } else { /* register */ @@ -901,10 +998,10 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_logic_T0_cc(); break; case 0x2: - gen_op_or_T1_T0(); - if (xop & 0x10) - gen_op_logic_T0_cc(); - break; + gen_op_or_T1_T0(); + if (xop & 0x10) + gen_op_logic_T0_cc(); + break; case 0x3: gen_op_xor_T1_T0(); if (xop & 0x10) @@ -964,9 +1061,14 @@ static void disas_sparc_insn(DisasContext * dc) default: goto illegal_insn; } - gen_movl_T0_reg(rd); + gen_movl_T0_reg(rd); } else { switch (xop) { + case 0x20: /* taddcc */ + case 0x21: /* tsubcc */ + case 0x22: /* taddcctv */ + case 0x23: /* tsubcctv */ + goto illegal_insn; case 0x24: /* mulscc */ gen_op_mulscc_T1_T0(); gen_movl_T0_reg(rd); @@ -1021,56 +1123,72 @@ static void disas_sparc_insn(DisasContext * dc) } break; #endif - case 0x38: /* jmpl */ - { - gen_op_add_T1_T0(); - gen_op_movl_npc_T0(); - if (rd != 0) { - gen_op_movl_T0_im((long) (dc->pc)); - gen_movl_T0_reg(rd); - } - dc->pc = dc->npc; - dc->npc = DYNAMIC_PC; - } - goto jmp_insn; -#if !defined(CONFIG_USER_ONLY) - case 0x39: /* rett */ - { - if (!supervisor(dc)) - goto priv_insn; - gen_op_add_T1_T0(); - gen_op_movl_npc_T0(); - gen_op_rett(); -#if 0 - dc->pc = dc->npc; - dc->npc = DYNAMIC_PC; + default: + goto illegal_insn; + } + } + } else { + rs1 = GET_FIELD(insn, 13, 17); + gen_movl_reg_T0(rs1); + if (IS_IMM) { /* immediate */ + rs2 = GET_FIELDs(insn, 19, 31); +#if defined(OPTIM) + if (rs2) { #endif - } -#if 0 - goto jmp_insn; + gen_movl_imm_T1(rs2); + gen_op_add_T1_T0(); +#if defined(OPTIM) + } #endif - break; + } else { /* register */ + rs2 = GET_FIELD(insn, 27, 31); +#if defined(OPTIM) + if (rs2) { +#endif + gen_movl_reg_T1(rs2); + gen_op_add_T1_T0(); +#if defined(OPTIM) + } #endif - case 0x3b: /* flush */ - gen_op_add_T1_T0(); - gen_op_flush_T0(); - break; - case 0x3c: /* save */ - save_state(dc); - gen_op_add_T1_T0(); - gen_op_save(); - gen_movl_T0_reg(rd); - break; - case 0x3d: /* restore */ - save_state(dc); - gen_op_add_T1_T0(); - gen_op_restore(); - gen_movl_T0_reg(rd); - break; - default: - goto illegal_insn; - } } + switch (xop) { + case 0x38: /* jmpl */ + { + gen_op_movl_npc_T0(); + if (rd != 0) { + gen_op_movl_T0_im((long) (dc->pc)); + gen_movl_T0_reg(rd); + } + dc->pc = dc->npc; + dc->npc = DYNAMIC_PC; + } + goto jmp_insn; +#if !defined(CONFIG_USER_ONLY) + case 0x39: /* rett */ + { + if (!supervisor(dc)) + goto priv_insn; + gen_op_movl_npc_T0(); + gen_op_rett(); + } + break; +#endif + case 0x3b: /* flush */ + gen_op_flush_T0(); + break; + case 0x3c: /* save */ + save_state(dc); + gen_op_save(); + gen_movl_T0_reg(rd); + break; + case 0x3d: /* restore */ + save_state(dc); + gen_op_restore(); + gen_movl_T0_reg(rd); + break; + default: + goto illegal_insn; + } } break; } @@ -1081,14 +1199,24 @@ static void disas_sparc_insn(DisasContext * dc) gen_movl_reg_T0(rs1); if (IS_IMM) { /* immediate */ rs2 = GET_FIELDs(insn, 19, 31); +#if defined(OPTIM) if (rs2 != 0) { +#endif gen_movl_imm_T1(rs2); gen_op_add_T1_T0(); +#if defined(OPTIM) } +#endif } else { /* register */ rs2 = GET_FIELD(insn, 27, 31); - gen_movl_reg_T1(rs2); - gen_op_add_T1_T0(); +#if defined(OPTIM) + if (rs2 != 0) { +#endif + gen_movl_reg_T1(rs2); + gen_op_add_T1_T0(); +#if defined(OPTIM) + } +#endif } if (xop < 4 || (xop > 7 && xop < 0x14) || \ (xop > 0x17 && xop < 0x20)) { @@ -1116,8 +1244,10 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_ldst(ldstub); break; case 0x0f: /* swap register with memory. Also atomically */ + gen_movl_reg_T1(rd); gen_op_ldst(swap); break; +#if !defined(CONFIG_USER_ONLY) case 0x10: /* load word alternate */ if (!supervisor(dc)) goto priv_insn; @@ -1157,11 +1287,18 @@ static void disas_sparc_insn(DisasContext * dc) case 0x1f: /* swap reg with alt. memory. Also atomically */ if (!supervisor(dc)) goto priv_insn; + gen_movl_reg_T1(rd); gen_op_swapa(insn, 1, 4, 0); break; +#endif + default: + goto illegal_insn; } gen_movl_T1_reg(rd); } else if (xop >= 0x20 && xop < 0x24) { +#if !defined(CONFIG_USER_ONLY) + gen_op_trap_ifnofpu(); +#endif switch (xop) { case 0x20: /* load fpreg */ gen_op_ldst(ldf); @@ -1169,11 +1306,14 @@ static void disas_sparc_insn(DisasContext * dc) break; case 0x21: /* load fsr */ gen_op_ldfsr(); + gen_op_store_FT0_fpr(rd); break; case 0x23: /* load double fpreg */ gen_op_ldst(lddf); gen_op_store_DT0_fpr(rd); break; + default: + goto illegal_insn; } } else if (xop < 8 || (xop >= 0x14 && xop < 0x18)) { gen_movl_reg_T1(rd); @@ -1192,6 +1332,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_movl_reg_T2(rd + 1); gen_op_ldst(std); break; +#if !defined(CONFIG_USER_ONLY) case 0x14: if (!supervisor(dc)) goto priv_insn; @@ -1214,24 +1355,37 @@ static void disas_sparc_insn(DisasContext * dc) gen_movl_reg_T2(rd + 1); gen_op_stda(insn, 0, 8, 0); break; +#endif + default: + goto illegal_insn; } } else if (xop > 0x23 && xop < 0x28) { +#if !defined(CONFIG_USER_ONLY) + gen_op_trap_ifnofpu(); +#endif switch (xop) { case 0x24: gen_op_load_fpr_FT0(rd); gen_op_ldst(stf); break; case 0x25: + gen_op_load_fpr_FT0(rd); gen_op_stfsr(); break; case 0x27: gen_op_load_fpr_DT0(rd); gen_op_ldst(stdf); break; + case 0x26: /* stdfq */ + default: + goto illegal_insn; } } else if (xop > 0x33 && xop < 0x38) { /* Co-processor */ + goto illegal_insn; } + else + goto illegal_insn; } } /* default case for non jump instructions */ @@ -1246,17 +1400,24 @@ static void disas_sparc_insn(DisasContext * dc) dc->pc = dc->npc; dc->npc = dc->npc + 4; } - jmp_insn:; + jmp_insn: return; illegal_insn: save_state(dc); gen_op_exception(TT_ILL_INSN); dc->is_br = 1; return; +#if !defined(CONFIG_USER_ONLY) priv_insn: save_state(dc); gen_op_exception(TT_PRIV_INSN); dc->is_br = 1; + return; +#endif + nfpu_insn: + save_state(dc); + gen_op_fpexception_im(FSR_FTT_UNIMPFPOP); + dc->is_br = 1; } static inline int gen_intermediate_code_internal(TranslationBlock * tb, @@ -1271,6 +1432,7 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb, dc->tb = tb; pc_start = tb->pc; dc->pc = pc_start; + last_pc = dc->pc; dc->npc = (target_ulong) tb->cs_base; #if defined(CONFIG_USER_ONLY) dc->mem_idx = 0; @@ -1285,8 +1447,13 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb, if (env->nb_breakpoints > 0) { for(j = 0; j < env->nb_breakpoints; j++) { if (env->breakpoints[j] == dc->pc) { - gen_debug(dc, dc->pc); - break; + if (dc->pc != pc_start) + save_state(dc); + gen_op_debug(); + gen_op_movl_T0_0(); + gen_op_exit_tb(); + dc->is_br = 1; + goto exit_gen_loop; } } } @@ -1310,8 +1477,18 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb, /* if the next PC is different, we abort now */ if (dc->pc != (last_pc + 4)) break; + /* if single step mode, we generate only one instruction and + generate an exception */ + if (env->singlestep_enabled) { + gen_op_jmp_im(dc->pc); + gen_op_movl_T0_0(); + gen_op_exit_tb(); + break; + } } while ((gen_opc_ptr < gen_opc_end) && (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32)); + + exit_gen_loop: if (!dc->is_br) { if (dc->pc != DYNAMIC_PC && (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) { @@ -1338,7 +1515,7 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb, } #endif } else { - tb->size = dc->npc - pc_start; + tb->size = last_pc + 4 - pc_start; } #ifdef DEBUG_DISAS if (loglevel & CPU_LOG_TB_IN_ASM) { @@ -1366,14 +1543,10 @@ int gen_intermediate_code_pc(CPUSPARCState * env, TranslationBlock * tb) return gen_intermediate_code_internal(tb, 1, env); } -CPUSPARCState *cpu_sparc_init(void) -{ - CPUSPARCState *env; - - cpu_exec_init(); +extern int ram_size; - if (!(env = malloc(sizeof(CPUSPARCState)))) - return (NULL); +void cpu_reset(CPUSPARCState *env) +{ memset(env, 0, sizeof(*env)); env->cwp = 0; env->wim = 1; @@ -1381,14 +1554,24 @@ CPUSPARCState *cpu_sparc_init(void) #if defined(CONFIG_USER_ONLY) env->user_mode_only = 1; #else - /* Emulate Prom */ env->psrs = 1; - env->pc = 0x4000; + env->pc = 0xffd00000; + env->gregs[1] = ram_size; + env->mmuregs[0] = (0x04 << 24); /* Impl 0, ver 4, MMU disabled */ env->npc = env->pc + 4; - env->mmuregs[0] = (0x10<<24) | MMU_E; /* Impl 1, ver 0, MMU Enabled */ - env->mmuregs[1] = 0x3000 >> 4; /* MMU Context table */ #endif +} + +CPUSPARCState *cpu_sparc_init(void) +{ + CPUSPARCState *env; + + cpu_exec_init(); + + if (!(env = malloc(sizeof(CPUSPARCState)))) + return (NULL); cpu_single_env = env; + cpu_reset(env); return (env); } @@ -1436,11 +1619,24 @@ void cpu_dump_state(CPUState *env, FILE *f, cpu_fprintf(f, "fsr: 0x%08x\n", env->fsr); } +#if defined(CONFIG_USER_ONLY) target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) { return addr; } +#else +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + uint32_t phys_addr; + int prot, access_index; + + if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2, 0) != 0) + return -1; + return phys_addr; +} +#endif + void helper_flush(target_ulong addr) { addr &= ~7; |