pax_global_header00006660000000000000000000000064137476042570014531gustar00rootroot0000000000000052 comment=74fcd9e0c2460f6bde58896117e6c1d31b2bafa0 blastem-0.6.3.4/000077500000000000000000000000001374760425700133305ustar00rootroot00000000000000blastem-0.6.3.4/.hgignore000066400000000000000000000010251374760425700151310ustar00rootroot00000000000000syntax: glob *.dis *.asm *.md *.jpg *.pdf *.tar.gz *.list *.wav *.z80 *~ arrow.png font_interlace_variable.png starscream/* gxz80 musashi/* vdpreverse/* nemesis/* html/* generated_tests/* ztests glew/* lib/* android/obj android/bin android/gen android/libs android/local.properties android/jni/SDL sdl *.o *.list *.eeprom *.sram *.zdis *.tiles *.dll *.exe *.orig *.gst *.log *.out *.lib *.vgm log*.txt *report*.txt output*.txt syntax: regexp ^blastem ^blastcpm ^dis ^stateview ^trans ^zdis ^ztestrun ^vgmplay ^vgmsplit ^[^/]*\.bin blastem-0.6.3.4/.hgtags000066400000000000000000000011601374760425700146040ustar00rootroot00000000000000949c7d8756931ca19d93d6de5cc507405a007937 v0.1.0 6b7a96d0eda8ed9f1a1436c3ac590a1c4db94f27 v0.2.0 0e5f14d9a57990a2c449a0d7c93250bbb4f6b9e5 v0.3.0 0e5f14d9a57990a2c449a0d7c93250bbb4f6b9e5 v0.3.0 283bdcd5bdb8cacaef8da9dfc72c56f6686dca29 v0.3.0 c9ed929ee9848de216e8d87f577b2be0e11637d9 v0.3.1 4a92889e2889bc3d7c2522a9e205d837d06bf59b v0.4.0 1ffa7891b4ec87a2b51afe012c7b5c001f37f780 v0.4.1 990a2639193394059355cd790c19eaf47eb376e9 v0.5.0 3d48cb0c28be9045866e00795b698086018b825f v0.5.1 ef50c9affe6a7c86398f2c36eb5439a559808108 v0.6.0 357b4951d9b2d1999e4c2765ee53e946aaab864d v0.6.1 8aeac7bd9fa7d9d978c99ec07e9a68989a12e453 v0.6.2 blastem-0.6.3.4/68kinst.c000066400000000000000000002416401374760425700150110ustar00rootroot00000000000000/* Copyright 2013 Michael Pavone This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "68kinst.h" #include #include uint32_t sign_extend16(uint32_t val) { return (val & 0x8000) ? val | 0xFFFF0000 : val; } uint32_t sign_extend8(uint32_t val) { return (val & 0x80) ? val | 0xFFFFFF00 : val; } uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t size, m68k_op_info *dst) { uint16_t ext, tmp; dst->addr_mode = mode; switch(mode) { case MODE_REG: case MODE_AREG: case MODE_AREG_INDIRECT: case MODE_AREG_POSTINC: case MODE_AREG_PREDEC: dst->params.regs.pri = reg; break; case MODE_AREG_DISPLACE: ext = *(++cur); dst->params.regs.pri = reg; dst->params.regs.displacement = sign_extend16(ext); break; case MODE_AREG_INDEX_MEM: dst->params.regs.pri = reg; ext = *(++cur); dst->params.regs.sec = ext >> 11;//includes areg/dreg bit, reg num and word/long bit #ifdef M68020 dst->params.regs.scale = ext >> 9 & 3; if (ext & 0x100) { dst->params.regs.disp_sizes = ext >> 4 & 3; switch (dst->params.regs.disp_sizes) { case 0: //reserved return NULL; case 1: dst->params.regs.displacement = 0; break; case 2: dst->params.regs.displacement = sign_extend16(*(cur++)); break; case 3: tmp = *(cur++); dst->params.regs.displacement = tmp << 16 | *(cur++); break; } if (ext & 0x3) { //memory indirect switch (ext & 0xC4) { case 0x00: dst->addr_mode = MODE_AREG_PREINDEX; break; case 0x04: dst->addr_mode = MODE_AREG_POSTINDEX; break; case 0x40: dst->addr_mode = MODE_AREG_MEM_INDIRECT; break; case 0x80: dst->addr_mode = MODE_PREINDEX; break; case 0x84: dst->addr_mode = MODE_POSTINDEX; break; case 0xC0: dst->addr_mode = MODE_MEM_INDIRECT; break; } dst->params.regs.disp_sizes |= ext << 4 & 0x30; switch (ext & 0x3) { case 0: //reserved return NULL; case 1: dst->params.regs.outer_disp = 0; break; case 2: dst->params.regs.outer_disp = sign_extend16(*(cur++)); break; case 3: tmp = *(cur++); dst->params.regs.outer_disp = tmp << 16 | *(cur++); break; } } else { switch (ext >> 6 & 3) { case 0: dst->addr_mode = MODE_AREG_INDEX_BASE_DISP; break; case 1: dst->addr_mode = MODE_AREG_BASE_DISP; break; case 2: dst->addr_mode = MODE_INDEX_BASE_DISP; break; case 3: dst->addr_mode = MODE_BASE_DISP; break; } } } else { #endif dst->addr_mode = MODE_AREG_INDEX_DISP8; dst->params.regs.displacement = sign_extend8(ext&0xFF); #ifdef M68020 } #endif break; case MODE_PC_INDIRECT_ABS_IMMED: switch(reg) { case 0: dst->addr_mode = MODE_ABSOLUTE_SHORT; ext = *(++cur); dst->params.immed = sign_extend16(ext); break; case 1: dst->addr_mode = MODE_ABSOLUTE; ext = *(++cur); dst->params.immed = ext << 16 | *(++cur); break; case 3: ext = *(++cur); dst->params.regs.sec = ext >> 11;//includes areg/dreg bit, reg num and word/long bit #ifdef M68020 dst->params.regs.scale = ext >> 9 & 3; if (ext & 0x100) { dst->params.regs.disp_sizes = ext >> 4 & 3; switch (dst->params.regs.disp_sizes) { case 0: //reserved return NULL; case 1: dst->params.regs.displacement = 0; break; case 2: dst->params.regs.displacement = sign_extend16(*(cur++)); break; case 3: tmp = *(cur++); dst->params.regs.displacement = tmp << 16 | *(cur++); break; } if (ext & 0x3) { //memory indirect switch (ext & 0xC4) { case 0x00: dst->addr_mode = MODE_PC_PREINDEX; break; case 0x04: dst->addr_mode = MODE_PC_POSTINDEX; break; case 0x40: dst->addr_mode = MODE_PC_MEM_INDIRECT; break; case 0x80: dst->addr_mode = MODE_ZPC_PREINDEX; break; case 0x84: dst->addr_mode = MODE_ZPC_POSTINDEX; break; case 0xC0: dst->addr_mode = MODE_ZPC_MEM_INDIRECT; break; } dst->params.regs.disp_sizes |= ext << 4 & 0x30; switch (ext & 0x3) { case 0: //reserved return NULL; case 1: dst->params.regs.outer_disp = 0; break; case 2: dst->params.regs.outer_disp = sign_extend16(*(cur++)); break; case 3: tmp = *(cur++); dst->params.regs.outer_disp = tmp << 16 | *(cur++); break; } } else { switch (ext >> 6 & 3) { case 0: dst->addr_mode = MODE_PC_INDEX_BASE_DISP; break; case 1: dst->addr_mode = MODE_PC_BASE_DISP; break; case 2: dst->addr_mode = MODE_ZPC_INDEX_BASE_DISP; break; case 3: dst->addr_mode = MODE_ZPC_BASE_DISP; break; } } } else { #endif dst->addr_mode = MODE_PC_INDEX_DISP8; dst->params.regs.displacement = sign_extend8(ext&0xFF); #ifdef M68020 } #endif break; case 2: dst->addr_mode = MODE_PC_DISPLACE; ext = *(++cur); dst->params.regs.displacement = sign_extend16(ext); break; case 4: dst->addr_mode = MODE_IMMEDIATE; ext = *(++cur); switch (size) { case OPSIZE_BYTE: dst->params.immed = ext & 0xFF; break; case OPSIZE_WORD: dst->params.immed = ext; break; case OPSIZE_LONG: dst->params.immed = ext << 16 | *(++cur); break; } break; default: return NULL; } break; } return cur; } uint8_t m68k_valid_immed_dst(m68k_op_info *dst) { if (dst->addr_mode == MODE_AREG || dst->addr_mode == MODE_IMMEDIATE) { return 0; } return 1; } uint8_t m68k_valid_immed_limited_dst(m68k_op_info *dst) { if (dst->addr_mode == MODE_AREG || dst->addr_mode > MODE_ABSOLUTE) { return 0; } return 1; } uint8_t m68k_valid_full_arith_dst(m68k_op_info *dst) { if (dst->addr_mode < MODE_AREG_INDIRECT || dst->addr_mode > MODE_ABSOLUTE) { return 0; } return 1; } uint8_t m68k_valid_movem_dst(m68k_op_info *dst) { if (dst->addr_mode == MODE_REG || dst->addr_mode == MODE_AREG_POSTINC) { return 0; } return m68k_valid_immed_limited_dst(dst); } uint16_t *m68k_decode_op(uint16_t *cur, uint8_t size, m68k_op_info *dst) { uint8_t mode = (*cur >> 3) & 0x7; uint8_t reg = *cur & 0x7; return m68k_decode_op_ex(cur, mode, reg, size, dst); } void m68k_decode_cond(uint16_t op, m68kinst * decoded) { decoded->extra.cond = (op >> 0x8) & 0xF; } uint8_t m68k_reg_quick_field(uint16_t op) { return (op >> 9) & 0x7; } uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) { uint16_t *start = istream; uint8_t optype = *istream >> 12; uint8_t size; uint8_t reg; uint8_t opmode; uint32_t immed; decoded->op = M68K_INVALID; decoded->src.addr_mode = decoded->dst.addr_mode = MODE_UNUSED; decoded->variant = VAR_NORMAL; decoded->address = address; switch(optype) { case BIT_MOVEP_IMMED: if ((*istream & 0x138) == 0x108) { //MOVEP decoded->op = M68K_MOVEP; decoded->extra.size = *istream & 0x40 ? OPSIZE_LONG : OPSIZE_WORD; if (*istream & 0x80) { //memory dest decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); decoded->dst.addr_mode = MODE_AREG_DISPLACE; decoded->dst.params.regs.pri = *istream & 0x7; decoded->dst.params.regs.displacement = *(++istream); } else { //memory source decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); decoded->src.addr_mode = MODE_AREG_DISPLACE; decoded->src.params.regs.pri = *istream & 0x7; decoded->src.params.regs.displacement = *(++istream); } } else if (*istream & 0x100) { //BTST, BCHG, BCLR, BSET switch ((*istream >> 6) & 0x3) { case 0: decoded->op = M68K_BTST; break; case 1: decoded->op = M68K_BCHG; break; case 2: decoded->op = M68K_BCLR; break; case 3: decoded->op = M68K_BSET; break; } decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); decoded->extra.size = OPSIZE_BYTE; istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->dst)); if ( !istream || decoded->dst.addr_mode == MODE_AREG || (decoded->op != M68K_BTST && !m68k_valid_immed_limited_dst(&decoded->dst)) ) { decoded->op = M68K_INVALID; break; } if (decoded->dst.addr_mode == MODE_REG) { decoded->extra.size = OPSIZE_LONG; } } else if ((*istream & 0xF00) == 0x800) { //BTST, BCHG, BCLR, BSET switch ((*istream >> 6) & 0x3) { case 0: decoded->op = M68K_BTST; break; case 1: decoded->op = M68K_BCHG; break; case 2: decoded->op = M68K_BCLR; break; case 3: decoded->op = M68K_BSET; break; } opmode = (*istream >> 3) & 0x7; reg = *istream & 0x7; decoded->src.addr_mode = MODE_IMMEDIATE_WORD; decoded->src.params.immed = *(++istream) & 0xFF; decoded->extra.size = OPSIZE_BYTE; istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); if ( !istream || !m68k_valid_immed_dst(&decoded->dst) || (decoded->op != M68K_BTST && !m68k_valid_immed_limited_dst(&decoded->dst)) ) { decoded->op = M68K_INVALID; break; } if (decoded->dst.addr_mode == MODE_REG) { decoded->extra.size = OPSIZE_LONG; } } else if ((*istream & 0xC0) == 0xC0) { #ifdef M68020 //CMP2, CHK2, CAS, CAS2, RTM, CALLM #endif } else { switch ((*istream >> 9) & 0x7) { case 0: if ((*istream & 0xFF) == 0x3C) { decoded->op = M68K_ORI_CCR; decoded->extra.size = OPSIZE_BYTE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream) & 0xFF; } else if((*istream & 0xFF) == 0x7C) { decoded->op = M68K_ORI_SR; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream); } else { decoded->op = M68K_OR; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = size = (*istream >> 6) & 3; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = immed << 16 | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } } break; case 1: //ANDI, ANDI to CCR, ANDI to SR if ((*istream & 0xFF) == 0x3C) { decoded->op = M68K_ANDI_CCR; decoded->extra.size = OPSIZE_BYTE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream) & 0xFF; } else if((*istream & 0xFF) == 0x7C) { decoded->op = M68K_ANDI_SR; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream); } else { decoded->op = M68K_AND; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = size = (*istream >> 6) & 3; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = immed << 16 | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } } break; case 2: decoded->op = M68K_SUB; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = size = (*istream >> 6) & 3; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = immed << 16 | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } break; case 3: decoded->op = M68K_ADD; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = size = (*istream >> 6) & 3; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = immed << 16 | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } break; case 4: //BTST, BCHG, BCLR, BSET //Seems like this should be unnecessary since bit instructions are explicitly handled above //Possible this is redundant or the case above is overly restrictive //TODO: Investigate whether this can be removed switch ((*istream >> 6) & 0x3) { case 0: decoded->op = M68K_BTST; break; case 1: decoded->op = M68K_BCHG; break; case 2: decoded->op = M68K_BCLR; break; case 3: decoded->op = M68K_BSET; break; } decoded->src.addr_mode = MODE_IMMEDIATE_WORD; decoded->src.params.immed = *(++istream) & 0xFF; istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst)); if ( !istream || !m68k_valid_immed_dst(&decoded->dst) || (decoded->op != M68K_BTST && !m68k_valid_immed_limited_dst(&decoded->dst)) ) { decoded->op = M68K_INVALID; break; } break; case 5: //EORI, EORI to CCR, EORI to SR if ((*istream & 0xFF) == 0x3C) { decoded->op = M68K_EORI_CCR; decoded->extra.size = OPSIZE_BYTE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream) & 0xFF; } else if((*istream & 0xFF) == 0x7C) { decoded->op = M68K_EORI_SR; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream); } else { decoded->op = M68K_EOR; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = size = (*istream >> 6) & 3; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = immed << 16 | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } } break; case 6: decoded->op = M68K_CMP; decoded->variant = VAR_IMMEDIATE; decoded->extra.size = (*istream >> 6) & 0x3; decoded->src.addr_mode = MODE_IMMEDIATE; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (decoded->extra.size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = (immed << 16) | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } break; case 7: #ifdef M68010 decoded->op = M68K_MOVES; decoded->extra.size = *istream >> 6 & 0x3; immed = *(++istream); reg = immed >> 12 & 0x7; opmode = immed & 0x8000 ? MODE_AREG : MODE_REG; if (immed & 0x800) { decoded->src.addr_mode = opmode; decoded->src.params.regs.pri = reg; m68k_decode_op_ex(istream, *start >> 3 & 0x7, *start & 0x7, decoded->extra.size, &(decoded->dst)); } else { m68k_decode_op_ex(istream, *start >> 3 & 0x7, *start & 0x7, decoded->extra.size, &(decoded->src)); decoded->dst.addr_mode = opmode; decoded->dst.params.regs.pri = reg; } #endif break; } } break; case MOVE_BYTE: case MOVE_LONG: case MOVE_WORD: decoded->op = M68K_MOVE; decoded->extra.size = optype == MOVE_BYTE ? OPSIZE_BYTE : (optype == MOVE_WORD ? OPSIZE_WORD : OPSIZE_LONG); opmode = (*istream >> 6) & 0x7; reg = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); if (!istream || (decoded->src.addr_mode == MODE_AREG && optype == MOVE_BYTE)) { decoded->op = M68K_INVALID; break; } istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); if (!istream || decoded->dst.addr_mode > MODE_ABSOLUTE || (decoded->dst.addr_mode == MODE_AREG && optype == MOVE_BYTE)) { decoded->op = M68K_INVALID; break; } break; case MISC: if ((*istream & 0x1C0) == 0x1C0) { decoded->op = M68K_LEA; decoded->extra.size = OPSIZE_LONG; decoded->dst.addr_mode = MODE_AREG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); if ( !istream || decoded->src.addr_mode == MODE_REG || decoded->src.addr_mode == MODE_AREG || decoded->src.addr_mode == MODE_AREG_POSTINC || decoded->src.addr_mode == MODE_AREG_PREDEC || decoded->src.addr_mode == MODE_IMMEDIATE ) { decoded->op = M68K_INVALID; break; } } else { if (*istream & 0x100) { decoded->op = M68K_CHK; if ((*istream & 0x180) == 0x180) { decoded->extra.size = OPSIZE_WORD; } else { //only on M68020+ #ifdef M68020 decoded->extra.size = OPSIZE_LONG; #else decoded->op = M68K_INVALID; break; #endif } decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } else { opmode = (*istream >> 3) & 0x7; if ((*istream & 0xB80) == 0x880 && opmode != MODE_REG && opmode != MODE_AREG) { //TODO: Check for invalid modes that are dependent on direction decoded->op = M68K_MOVEM; decoded->extra.size = *istream & 0x40 ? OPSIZE_LONG : OPSIZE_WORD; reg = *istream & 0x7; if(*istream & 0x400) { decoded->dst.addr_mode = MODE_REG; decoded->dst.params.immed = *(++istream); istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG_PREDEC || decoded->src.addr_mode == MODE_IMMEDIATE) { decoded->op = M68K_INVALID; break; } if (decoded->src.addr_mode == MODE_PC_DISPLACE || decoded->src.addr_mode == MODE_PC_INDEX_DISP8) { //adjust displacement to account for extra instruction word decoded->src.params.regs.displacement += 2; } } else { decoded->src.addr_mode = MODE_REG; decoded->src.params.immed = *(++istream); istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); if (!istream || !m68k_valid_movem_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } } else { optype = (*istream >> 9) & 0x7; size = (*istream >> 6) & 0x3; switch(optype) { case 0: //Move from SR or NEGX if (size == OPSIZE_INVALID) { decoded->op = M68K_MOVE_FROM_SR; size = OPSIZE_WORD; } else { decoded->op = M68K_NEGX; } decoded->extra.size = size; istream= m68k_decode_op(istream, size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } break; case 1: //MOVE from CCR or CLR if (size == OPSIZE_INVALID) { #ifdef M68010 decoded->op = M68K_MOVE_FROM_CCR; size = OPSIZE_WORD; #else break; #endif } else { decoded->op = M68K_CLR; } decoded->extra.size = size; istream= m68k_decode_op(istream, size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } break; case 2: //MOVE to CCR or NEG if (size == OPSIZE_INVALID) { decoded->op = M68K_MOVE_CCR; size = OPSIZE_WORD; istream= m68k_decode_op(istream, size, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } else { decoded->op = M68K_NEG; istream= m68k_decode_op(istream, size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } decoded->extra.size = size; break; case 3: //MOVE to SR or NOT if (size == OPSIZE_INVALID) { decoded->op = M68K_MOVE_SR; size = OPSIZE_WORD; istream= m68k_decode_op(istream, size, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } else { decoded->op = M68K_NOT; istream= m68k_decode_op(istream, size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } decoded->extra.size = size; break; case 4: //EXT, EXTB, LINK.l, NBCD, SWAP, BKPT, PEA switch((*istream >> 3) & 0x3F) { case 1: #ifdef M68020 decoded->op = M68K_LINK; decoded->extra.size = OPSIZE_LONG; reg = *istream & 0x7; immed = *(++istream) << 16; immed |= *(++istream); #endif break; case 8: decoded->op = M68K_SWAP; decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = *istream & 0x7; decoded->extra.size = OPSIZE_WORD; break; case 9: #ifdef M68010 decoded->op = M68K_BKPT; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = OPSIZE_UNSIZED; decoded->src.params.immed = *istream & 0x7; #endif break; case 0x10: decoded->op = M68K_EXT; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = *istream & 0x7; decoded->extra.size = OPSIZE_WORD; break; case 0x18: decoded->op = M68K_EXT; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = *istream & 0x7; decoded->extra.size = OPSIZE_LONG; break; case 0x38: #ifdef M68020 #endif break; default: if (!(*istream & 0x1C0)) { decoded->op = M68K_NBCD; decoded->extra.size = OPSIZE_BYTE; istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } else if((*istream & 0x1C0) == 0x40) { decoded->op = M68K_PEA; decoded->extra.size = OPSIZE_LONG; istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src)); if ( !istream || decoded->src.addr_mode == MODE_REG || decoded->src.addr_mode == MODE_AREG || decoded->src.addr_mode == MODE_AREG_POSTINC || decoded->src.addr_mode == MODE_AREG_PREDEC || decoded->src.addr_mode == MODE_IMMEDIATE ) { decoded->op = M68K_INVALID; break; } } } break; case 5: //BGND, ILLEGAL, TAS, TST optype = *istream & 0xFF; if (optype == 0xFA) { //BGND - CPU32 only } else if (optype == 0xFC) { decoded->op = M68K_ILLEGAL; decoded->extra.size = OPSIZE_UNSIZED; } else { if (size == OPSIZE_INVALID) { decoded->op = M68K_TAS; decoded->extra.size = OPSIZE_BYTE; istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } else { decoded->op = M68K_TST; decoded->extra.size = size; istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; break; } #ifndef M68020 if (!m68k_valid_immed_limited_dst(&decoded->src)) { decoded->op = M68K_INVALID; break; } #endif } } break; case 6: //MULU, MULS, DIVU, DIVUL, DIVS, DIVSL #ifdef M68020 //TODO: Implement these for 68020+ support #endif break; case 7: //TRAP, LINK.w, UNLNK, MOVE USP, RESET, NOP, STOP, RTE, RTD, RTS, TRAPV, RTR, MOVEC, JSR, JMP if (*istream & 0x80) { //JSR, JMP if (*istream & 0x40) { decoded->op = M68K_JMP; } else { decoded->op = M68K_JSR; } decoded->extra.size = OPSIZE_UNSIZED; istream = m68k_decode_op(istream, OPSIZE_UNSIZED, &(decoded->src)); if ( !istream || (decoded->src.addr_mode < MODE_AREG_DISPLACE && decoded->src.addr_mode != MODE_AREG_INDIRECT) || decoded->src.addr_mode == MODE_IMMEDIATE ) { decoded->op = M68K_INVALID; break; } } else { //it would appear bit 6 needs to be set for it to be a valid instruction here if (!(*istream & 0x40)) { decoded->op = M68K_INVALID; break; } switch((*istream >> 3) & 0x7) { case 0: case 1: //TRAP decoded->op = M68K_TRAP; decoded->extra.size = OPSIZE_UNSIZED; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *istream & 0xF; break; case 2: //LINK.w decoded->op = M68K_LINK; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_AREG; decoded->src.params.regs.pri = *istream & 0x7; decoded->dst.addr_mode = MODE_IMMEDIATE; decoded->dst.params.immed = sign_extend16(*(++istream)); break; case 3: //UNLK decoded->op = M68K_UNLK; decoded->extra.size = OPSIZE_UNSIZED; decoded->dst.addr_mode = MODE_AREG; decoded->dst.params.regs.pri = *istream & 0x7; break; case 4: case 5: //MOVE USP decoded->op = M68K_MOVE_USP; if (*istream & 0x8) { decoded->dst.addr_mode = MODE_AREG; decoded->dst.params.regs.pri = *istream & 0x7; } else { decoded->src.addr_mode = MODE_AREG; decoded->src.params.regs.pri = *istream & 0x7; } break; case 6: decoded->extra.size = OPSIZE_UNSIZED; switch(*istream & 0x7) { case 0: decoded->op = M68K_RESET; break; case 1: decoded->op = M68K_NOP; break; case 2: decoded->op = M68K_STOP; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed =*(++istream); break; case 3: decoded->op = M68K_RTE; break; case 4: #ifdef M68010 decoded->op = M68K_RTD; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed =*(++istream); #endif break; case 5: decoded->op = M68K_RTS; break; case 6: decoded->op = M68K_TRAPV; break; case 7: decoded->op = M68K_RTR; break; } break; case 7: //MOVEC #ifdef M68010 decoded->op = M68K_MOVEC; immed = *(++istream); reg = immed >> 12 & 0x7; opmode = immed & 0x8000 ? MODE_AREG : MODE_REG; immed &= 0xFFF; if (immed & 0x800) { if (immed > MAX_HIGH_CR) { decoded->op = M68K_INVALID; break; } else { immed = immed - 0x800 + CR_USP; } } else { if (immed > MAX_LOW_CR) { decoded->op = M68K_INVALID; break; } } if (*start & 1) { decoded->src.addr_mode = opmode; decoded->src.params.regs.pri = reg; decoded->dst.params.immed = immed; } else { decoded->dst.addr_mode = opmode; decoded->dst.params.regs.pri = reg; decoded->src.params.immed = immed; } #endif break; } } break; } } } } break; case QUICK_ARITH_LOOP: size = (*istream >> 6) & 3; if (size == 0x3) { //DBcc, TRAPcc or Scc m68k_decode_cond(*istream, decoded); if (((*istream >> 3) & 0x7) == 1) { decoded->op = M68K_DBCC; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = *istream & 0x7; decoded->src.params.immed = sign_extend16(*(++istream)); } else if(((*istream >> 3) & 0x7) == 1 && (*istream & 0x7) > 1 && (*istream & 0x7) < 5) { #ifdef M68020 decoded->op = M68K_TRAPCC; decoded->src.addr_mode = MODE_IMMEDIATE; //TODO: Figure out what to do with OPMODE and optional extention words #endif } else { decoded->op = M68K_SCC; decoded->extra.cond = (*istream >> 8) & 0xF; istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst)); if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } } else { //ADDQ, SUBQ decoded->variant = VAR_QUICK; decoded->extra.size = size; decoded->src.addr_mode = MODE_IMMEDIATE; immed = m68k_reg_quick_field(*istream); if (!immed) { immed = 8; } decoded->src.params.immed = immed; if (*istream & 0x100) { decoded->op = M68K_SUB; } else { decoded->op = M68K_ADD; } istream = m68k_decode_op(istream, size, &(decoded->dst)); if (!istream || decoded->dst.addr_mode > MODE_ABSOLUTE || (size == OPSIZE_BYTE && decoded->dst.addr_mode == MODE_AREG)) { decoded->op = M68K_INVALID; break; } } break; case BRANCH: m68k_decode_cond(*istream, decoded); decoded->op = decoded->extra.cond == COND_FALSE ? M68K_BSR : M68K_BCC; decoded->src.addr_mode = MODE_IMMEDIATE; immed = *istream & 0xFF; if (immed == 0) { decoded->variant = VAR_WORD; immed = *(++istream); immed = sign_extend16(immed); #ifdef M68020 } else if (immed == 0xFF) { decoded->variant = VAR_LONG; immed = *(++istream) << 16; immed |= *(++istream); #endif } else { decoded->variant = VAR_BYTE; immed = sign_extend8(immed); } decoded->src.params.immed = immed; break; case MOVEQ: if (*istream & 0x100) { decoded->op = M68K_INVALID; break; } decoded->op = M68K_MOVE; decoded->variant = VAR_QUICK; decoded->extra.size = OPSIZE_LONG; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = sign_extend8(*istream & 0xFF); decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); immed = *istream & 0xFF; break; case OR_DIV_SBCD: //for OR, if opmode bit 2 is 1, then src = Dn, dst = opmode = (*istream >> 6) & 0x7; size = opmode & 0x3; if (size == OPSIZE_INVALID || (opmode & 0x4 && !(*istream & 0x30))) { switch(opmode) { case 3: decoded->op = M68K_DIVU; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } break; case 4: decoded->op = M68K_SBCD; decoded->extra.size = OPSIZE_BYTE; decoded->dst.addr_mode = decoded->src.addr_mode = *istream & 0x8 ? MODE_AREG_PREDEC : MODE_REG; decoded->src.params.regs.pri = *istream & 0x7; decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; break; case 5: #ifdef M68020 #endif break; case 6: #ifdef M68020 #endif break; case 7: decoded->op = M68K_DIVS; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } break; } } else { decoded->op = M68K_OR; decoded->extra.size = size; if (opmode & 0x4) { decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = (*istream >> 9) & 0x7; istream = m68k_decode_op(istream, size, &(decoded->dst)); if (!istream || !m68k_valid_full_arith_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } } else { decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; istream = m68k_decode_op(istream, size, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } } break; case SUB_SUBX: size = (*istream >> 6) & 0x3; decoded->op = M68K_SUB; if (*istream & 0x100) { // destination, SUBA.l or SUBX if (*istream & 0x30 || size == OPSIZE_INVALID) { if (size == OPSIZE_INVALID) { //SUBA.l decoded->extra.size = OPSIZE_LONG; decoded->dst.addr_mode = MODE_AREG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; break; } } else { decoded->extra.size = size; decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, size, &(decoded->dst)); if (!istream || !m68k_valid_full_arith_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } } else { //SUBX decoded->op = M68K_SUBX; decoded->extra.size = size; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); decoded->src.params.regs.pri = *istream & 0x7; if (*istream & 0x8) { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_AREG_PREDEC; } else { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_REG; } } } else { if (size == OPSIZE_INVALID) { //SUBA.w decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_AREG; } else { decoded->extra.size = size; decoded->dst.addr_mode = MODE_REG; } decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); if (!istream || (decoded->src.addr_mode == MODE_AREG && decoded->extra.size == OPSIZE_BYTE)) { decoded->op = M68K_INVALID; break; } } break; case A_LINE: decoded->op = M68K_A_LINE_TRAP; break; case CMP_XOR: size = (*istream >> 6) & 0x3; decoded->op = M68K_CMP; if (*istream & 0x100) { //CMPM or CMPA.l or EOR if (size == OPSIZE_INVALID) { decoded->extra.size = OPSIZE_LONG; decoded->dst.addr_mode = MODE_AREG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; break; } } else { reg = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; break; } decoded->extra.size = size; if (decoded->dst.addr_mode == MODE_AREG) { //CMPM decoded->src.addr_mode = decoded->dst.addr_mode = MODE_AREG_POSTINC; decoded->src.params.regs.pri = decoded->dst.params.regs.pri; decoded->dst.params.regs.pri = reg; } else if (!m68k_valid_immed_limited_dst(&decoded->dst)){ decoded->op = M68K_INVALID; break; } else { //EOR decoded->op = M68K_EOR; decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = reg; } } } else { //CMP or CMPA.w if (size == OPSIZE_INVALID) { decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_AREG; } else { decoded->extra.size = size; decoded->dst.addr_mode = MODE_REG; } decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); if (!istream || (decoded->src.addr_mode == MODE_AREG && decoded->extra.size == OPSIZE_BYTE)) { decoded->op = M68K_INVALID; break; } } break; case AND_MUL_ABCD_EXG: //page 575 for summary //EXG opmodes: //01000 -data regs //01001 -addr regs //10001 -one of each //AND opmodes: //operand order bit + 2 size bits (00 - 10) //no address register direct addressing //data register direct not allowed when is the source (operand order bit of 1) if (*istream & 0x100) { if ((*istream & 0xC0) == 0xC0) { decoded->op = M68K_MULS; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } else if(!(*istream & 0xF0)) { decoded->op = M68K_ABCD; decoded->extra.size = OPSIZE_BYTE; decoded->src.params.regs.pri = *istream & 0x7; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); decoded->dst.addr_mode = decoded->src.addr_mode = (*istream & 8) ? MODE_AREG_PREDEC : MODE_REG; } else if(!(*istream & 0x30)) { decoded->op = M68K_EXG; decoded->extra.size = OPSIZE_LONG; decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); decoded->dst.params.regs.pri = *istream & 0x7; if (*istream & 0x8) { if (*istream & 0x80) { decoded->src.addr_mode = MODE_REG; decoded->dst.addr_mode = MODE_AREG; } else { decoded->src.addr_mode = decoded->dst.addr_mode = MODE_AREG; } } else if (*istream & 0x40) { decoded->src.addr_mode = decoded->dst.addr_mode = MODE_REG; } else { decoded->op = M68K_INVALID; break; } } else { decoded->op = M68K_AND; decoded->extra.size = (*istream >> 6) & 0x3; decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->dst)); if (!istream || !m68k_valid_full_arith_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } } } else { if ((*istream & 0xC0) == 0xC0) { decoded->op = M68K_MULU; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } else { decoded->op = M68K_AND; decoded->extra.size = (*istream >> 6) & 0x3; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } } break; case ADD_ADDX: size = (*istream >> 6) & 0x3; decoded->op = M68K_ADD; if (*istream & 0x100) { // destination, ADDA.l or ADDX if (*istream & 0x30 || size == OPSIZE_INVALID) { if (size == OPSIZE_INVALID) { //ADDA.l decoded->extra.size = OPSIZE_LONG; decoded->dst.addr_mode = MODE_AREG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; break; } } else { decoded->extra.size = size; decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, size, &(decoded->dst)); if (!istream || !m68k_valid_full_arith_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } } else { //ADDX decoded->op = M68K_ADDX; decoded->extra.size = size; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); decoded->src.params.regs.pri = *istream & 0x7; if (*istream & 0x8) { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_AREG_PREDEC; } else { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_REG; } } } else { if (size == OPSIZE_INVALID) { //ADDA.w decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_AREG; } else { decoded->extra.size = size; decoded->dst.addr_mode = MODE_REG; } decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); if (!istream || (decoded->src.addr_mode == MODE_AREG && decoded->extra.size == OPSIZE_BYTE)) { decoded->op = M68K_INVALID; break; } } break; case SHIFT_ROTATE: if ((*istream & 0x8C0) == 0xC0) { switch((*istream >> 8) & 0x7) { case 0: decoded->op = M68K_ASR; break; case 1: decoded->op = M68K_ASL; break; case 2: decoded->op = M68K_LSR; break; case 3: decoded->op = M68K_LSL; break; case 4: decoded->op = M68K_ROXR; break; case 5: decoded->op = M68K_ROXL; break; case 6: decoded->op = M68K_ROR; break; case 7: decoded->op = M68K_ROL; break; } decoded->extra.size = OPSIZE_WORD; istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->dst)); if (!istream || !m68k_valid_full_arith_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } else if((*istream & 0xC0) != 0xC0) { switch(((*istream >> 2) & 0x6) | ((*istream >> 8) & 1)) { case 0: decoded->op = M68K_ASR; break; case 1: decoded->op = M68K_ASL; break; case 2: decoded->op = M68K_LSR; break; case 3: decoded->op = M68K_LSL; break; case 4: decoded->op = M68K_ROXR; break; case 5: decoded->op = M68K_ROXL; break; case 6: decoded->op = M68K_ROR; break; case 7: decoded->op = M68K_ROL; break; } decoded->extra.size = (*istream >> 6) & 0x3; immed = (*istream >> 9) & 0x7; if (*istream & 0x20) { decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = immed; } else { decoded->src.addr_mode = MODE_IMMEDIATE; if (!immed) { immed = 8; } decoded->src.params.immed = immed; decoded->variant = VAR_QUICK; } decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = *istream & 0x7; } else { #ifdef M68020 //TODO: Implement bitfield instructions for M68020+ support switch (*istream >> 8 & 7) { case 0: decoded->op = M68K_BFTST; // break; case 1: decoded->op = M68K_BFEXTU; //, Dn break; case 2: decoded->op = M68K_BFCHG; // break; case 3: decoded->op = M68K_BFEXTS; //, Dn break; case 4: decoded->op = M68K_BFCLR; // break; case 5: decoded->op = M68K_BFFFO; //, Dn break; case 6: decoded->op = M68K_BFSET; // break; case 7: decoded->op = M68K_BFINS; //Dn, break; } opmode = *istream >> 3 & 0x7; reg = *istream & 0x7; m68k_op_info *ea, *other; if (decoded->op == M68K_BFEXTU || decoded->op == M68K_BFEXTS || decoded->op == M68K_BFFFO) { ea = &(decoded->src); other = &(decoded->dst); } else { ea = &(decoded->dst); other = &(decoded->dst); } if (*istream & 0x100) { immed = *(istream++); other->addr_mode = MODE_REG; other->params.regs.pri = immed >> 12 & 0x7; } else { immed = *(istream++); } decoded->extra.size = OPSIZE_UNSIZED; istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, ea); ea->addr_mode |= M68K_FLAG_BITFIELD; ea->bitfield = immed & 0xFFF; #endif } break; case F_LINE: //TODO: Decode FPU instructions for members of the 68K family with an FPU decoded->op = M68K_F_LINE_TRAP; break; } if (decoded->op == M68K_INVALID) { decoded->src.params.immed = *start; decoded->bytes = 2; return start + 1; } decoded->bytes = 2 * (istream + 1 - start); return istream+1; } uint32_t m68k_branch_target(m68kinst * inst, uint32_t *dregs, uint32_t *aregs) { if(inst->op == M68K_BCC || inst->op == M68K_BSR || inst->op == M68K_DBCC) { return inst->address + 2 + inst->src.params.immed; } else if(inst->op == M68K_JMP || inst->op == M68K_JSR) { uint32_t ret = 0; switch(inst->src.addr_mode) { case MODE_AREG_INDIRECT: ret = aregs[inst->src.params.regs.pri]; break; case MODE_AREG_DISPLACE: ret = aregs[inst->src.params.regs.pri] + inst->src.params.regs.displacement; break; case MODE_AREG_INDEX_DISP8: { uint8_t sec_reg = inst->src.params.regs.sec >> 1 & 0x7; ret = aregs[inst->src.params.regs.pri]; uint32_t * regfile = inst->src.params.regs.sec & 0x10 ? aregs : dregs; if (inst->src.params.regs.sec & 1) { //32-bit index register ret += regfile[sec_reg]; } else { //16-bit index register if (regfile[sec_reg] & 0x8000) { ret += (0xFFFF0000 | regfile[sec_reg]); } else { ret += regfile[sec_reg]; } } ret += inst->src.params.regs.displacement; break; } case MODE_PC_DISPLACE: ret = inst->src.params.regs.displacement + inst->address + 2; break; case MODE_PC_INDEX_DISP8: { uint8_t sec_reg = inst->src.params.regs.sec >> 1 & 0x7; ret = inst->address + 2; uint32_t * regfile = inst->src.params.regs.sec & 0x10 ? aregs : dregs; if (inst->src.params.regs.sec & 1) { //32-bit index register ret += regfile[sec_reg]; } else { //16-bit index register if (regfile[sec_reg] & 0x8000) { ret += (0xFFFF0000 | regfile[sec_reg]); } else { ret += regfile[sec_reg]; } } ret += inst->src.params.regs.displacement; break; } case MODE_ABSOLUTE: case MODE_ABSOLUTE_SHORT: ret = inst->src.params.immed; break; } return ret; } return 0; } uint8_t m68k_is_branch(m68kinst * inst) { return (inst->op == M68K_BCC && inst->extra.cond != COND_FALSE) || (inst->op == M68K_DBCC && inst->extra.cond != COND_TRUE) || inst->op == M68K_BSR || inst->op == M68K_JMP || inst->op == M68K_JSR; } uint8_t m68k_is_noncall_branch(m68kinst * inst) { return m68k_is_branch(inst) && inst->op != M68K_BSR && inst->op != M68K_JSR; } char * mnemonics[] = { "abcd", "add", "addx", "and", "andi",//ccr "andi",//sr "asl", "asr", "bcc", "bchg", "bclr", "bset", "bsr", "btst", "chk", "clr", "cmp", "dbcc", "divs", "divu", "eor", "eori",//ccr "eori",//sr "exg", "ext", "illegal", "jmp", "jsr", "lea", "link", "lsl", "lsr", "move", "move",//ccr "move",//from_sr "move",//sr "move",//usp "movem", "movep", "muls", "mulu", "nbcd", "neg", "negx", "nop", "not", "or", "ori",//ccr "ori",//sr "pea", "reset", "rol", "ror", "roxl", "roxr", "rte", "rtr", "rts", "sbcd", "scc", "stop", "sub", "subx", "swap", "tas", "trap", "trapv", "tst", "unlk", "invalid", #ifdef M68010 "bkpt", "move", //from ccr "movec", "moves", "rtd", #endif #ifdef M68020 "bfchg", "bfclr", "bfexts", "bfextu", "bfffo", "bfins", "bfset", "bftst", "callm", "cas", "cas2", "chk2", "cmp2", "cpbcc", "cpdbcc", "cpgen", "cprestore", "cpsave", "cpscc", "cptrapcc", "divsl", "divul", "extb", "pack", "rtm", "trapcc", "unpk" #endif }; char * cond_mnem[] = { "ra", "f", "hi", "ls", "cc", "cs", "ne", "eq", "vc", "vs", "pl", "mi", "ge", "lt", "gt", "le" }; #ifdef M68010 char * cr_mnem[] = { "SFC", "DFC", #ifdef M68020 "CACR", #endif "USP", "VBR", #ifdef M68020 "CAAR", "MSP", "ISP" #endif }; #endif int m68k_disasm_op(m68k_op_info *decoded, char *dst, int need_comma, uint8_t labels, uint32_t address, format_label_fun label_fun, void * data) { char * c = need_comma ? "," : ""; int ret = 0; #ifdef M68020 uint8_t addr_mode = decoded->addr_mode & (~M68K_FLAG_BITFIELD); #else uint8_t addr_mode = decoded->addr_mode; #endif switch(addr_mode) { case MODE_REG: ret = sprintf(dst, "%s d%d", c, decoded->params.regs.pri); break; case MODE_AREG: ret = sprintf(dst, "%s a%d", c, decoded->params.regs.pri); break; case MODE_AREG_INDIRECT: ret = sprintf(dst, "%s (a%d)", c, decoded->params.regs.pri); break; case MODE_AREG_POSTINC: ret = sprintf(dst, "%s (a%d)+", c, decoded->params.regs.pri); break; case MODE_AREG_PREDEC: ret = sprintf(dst, "%s -(a%d)", c, decoded->params.regs.pri); break; case MODE_AREG_DISPLACE: ret = sprintf(dst, "%s (%d, a%d)", c, decoded->params.regs.displacement, decoded->params.regs.pri); break; case MODE_AREG_INDEX_DISP8: #ifdef M68020 if (decoded->params.regs.scale) { ret = sprintf(dst, "%s (%d, a%d, %c%d.%c*%d)", c, decoded->params.regs.displacement, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); } else { #endif ret = sprintf(dst, "%s (%d, a%d, %c%d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w'); #ifdef M68020 } #endif break; #ifdef M68020 case MODE_AREG_INDEX_BASE_DISP: if (decoded->params.regs.disp_sizes > 1) { ret = sprintf(dst, "%s (%d.%c, a%d, %c%d.%c*%d)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes == 2 ? 'w' : 'l', decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); } else { ret = sprintf(dst, "%s (a%d, %c%d.%c*%d)", c, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); } break; case MODE_AREG_PREINDEX: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([a%d, %c%d.%c*%d])", c, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c, a%d, %c%d.%c*%d])", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([a%d, %c%d.%c*%d], %d.%c)", c, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c, a%d, %c%d.%c*%d], %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_AREG_POSTINDEX: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([a%d], %c%d.%c*%d)", c, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c, a%d], %c%d.%c*%d)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([a%d], %c%d.%c*%d, %d.%c)", c, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c, a%d], %c%d.%c*%d, %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_AREG_MEM_INDIRECT: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([a%d])", c, decoded->params.regs.pri); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c, a%d])", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([a%d], %d.%c)", c, decoded->params.regs.pri, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c, a%d], %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.pri, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_AREG_BASE_DISP: if (decoded->params.regs.disp_sizes > 1) { ret = sprintf(dst, "%s (%d.%c, a%d)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes == 2 ? 'w' : 'l', decoded->params.regs.pri); } else { //this is a lossy representation of the encoded instruction //not sure if there's a better way to print it though ret = sprintf(dst, "%s (a%d)", c, decoded->params.regs.pri); } break; case MODE_INDEX_BASE_DISP: if (decoded->params.regs.disp_sizes > 1) { ret = sprintf(dst, "%s (%d.%c, %c%d.%c*%d)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); } else { ret = sprintf(dst, "%s (%c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); } break; case MODE_PREINDEX: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([%c%d.%c*%d])", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c, %c%d.%c*%d])", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([%c%d.%c*%d], %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c, %c%d.%c*%d], %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_POSTINDEX: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([], %c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c], %c%d.%c*%d)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([], %c%d.%c*%d, %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c], %c%d.%c*%d, %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_MEM_INDIRECT: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([])", c); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c])", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l'); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([], %d.%c)", c, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c], %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_BASE_DISP: if (decoded->params.regs.disp_sizes > 1) { ret = sprintf(dst, "%s (%d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l'); } else { ret = sprintf(dst, "%s ()", c); } break; #endif case MODE_IMMEDIATE: case MODE_IMMEDIATE_WORD: ret = sprintf(dst, (decoded->params.immed <= 128 ? "%s #%d" : "%s #$%X"), c, decoded->params.immed); break; case MODE_ABSOLUTE_SHORT: if (labels) { ret = sprintf(dst, "%s ", c); ret += label_fun(dst+ret, decoded->params.immed, data); strcat(dst+ret, ".w"); ret = ret + 2; } else { ret = sprintf(dst, "%s $%X.w", c, decoded->params.immed); } break; case MODE_ABSOLUTE: if (labels) { ret = sprintf(dst, "%s ", c); ret += label_fun(dst+ret, decoded->params.immed, data); strcat(dst+ret, ".l"); ret = ret + 2; } else { ret = sprintf(dst, "%s $%X", c, decoded->params.immed); } break; case MODE_PC_DISPLACE: if (labels) { ret = sprintf(dst, "%s ", c); ret += label_fun(dst+ret, address + 2 + decoded->params.regs.displacement, data); strcat(dst+ret, "(pc)"); ret = ret + 4; } else { ret = sprintf(dst, "%s (%d, pc)", c, decoded->params.regs.displacement); } break; case MODE_PC_INDEX_DISP8: #ifdef M68020 if (decoded->params.regs.scale) { ret = sprintf(dst, "%s (%d, pc, %c%d.%c*%d)", c, decoded->params.regs.displacement, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); } else { #endif ret = sprintf(dst, "%s (%d, pc, %c%d.%c)", c, decoded->params.regs.displacement, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w'); #ifdef M68020 } #endif break; #ifdef M68020 case MODE_PC_INDEX_BASE_DISP: if (decoded->params.regs.disp_sizes > 1) { ret = sprintf(dst, "%s (%d.%c, pc, %c%d.%c*%d)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); } else { ret = sprintf(dst, "%s (pc, %c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); } break; case MODE_PC_PREINDEX: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([pc, %c%d.%c*%d])", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c, pc, %c%d.%c*%d])", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([pc, %c%d.%c*%d], %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c, pc, %c%d.%c*%d], %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_PC_POSTINDEX: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([pc], %c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c, pc], %c%d.%c*%d)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([pc], %c%d.%c*%d, %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c, pc], %c%d.%c*%d, %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_PC_MEM_INDIRECT: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([pc])", c); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c, pc])", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l'); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([pc], %d.%c)", c, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c, pc], %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_PC_BASE_DISP: if (decoded->params.regs.disp_sizes > 1) { ret = sprintf(dst, "%s (%d.%c, pc)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l'); } else { ret = sprintf(dst, "%s (pc)", c); } break; case MODE_ZPC_INDEX_BASE_DISP: if (decoded->params.regs.disp_sizes > 1) { ret = sprintf(dst, "%s (%d.%c, zpc, %c%d.%c*%d)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); } else { ret = sprintf(dst, "%s (zpc, %c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); } break; case MODE_ZPC_PREINDEX: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([zpc, %c%d.%c*%d])", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c, zpc, %c%d.%c*%d])", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([zpc, %c%d.%c*%d], %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c, zpc, %c%d.%c*%d], %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_ZPC_POSTINDEX: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([zpc], %c%d.%c*%d)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c, zpc], %c%d.%c*%d)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([zpc], %c%d.%c*%d, %d.%c)", c, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c, zpc], %c%d.%c*%d, %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w', 1 << decoded->params.regs.scale, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_ZPC_MEM_INDIRECT: switch (decoded->params.regs.disp_sizes) { case 0x11: //no base displacement or outer displacement ret = sprintf(dst, "%s ([zpc])", c); break; case 0x12: case 0x13: //base displacement only ret = sprintf(dst, "%s ([%d.%c, zpc])", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l'); break; case 0x21: case 0x31: //outer displacement only ret = sprintf(dst, "%s ([zpc], %d.%c)", c, decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; case 0x22: case 0x23: case 0x32: case 0x33: //both outer and inner displacement ret = sprintf(dst, "%s ([%d.%c, zpc], %d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l', decoded->params.regs.outer_disp, decoded->params.regs.disp_sizes & 0x30 == 0x20 ? 'w' : 'l'); break; } break; case MODE_ZPC_BASE_DISP: if (decoded->params.regs.disp_sizes > 1) { ret = sprintf(dst, "%s (%d.%c, zpc)", c, decoded->params.regs.displacement, decoded->params.regs.disp_sizes & 3 == 2 ? 'w' : 'l'); } else { ret = sprintf(dst, "%s (zpc)", c); } break; #endif default: ret = 0; } #ifdef M68020 if (decoded->addr_mode & M68K_FLAG_BITFIELD) { switch (decoded->bitfield & 0x820) { case 0: return ret + sprintf(dst+ret, " {$%X:%d}", decoded->bitfield >> 6 & 0x1F, decoded->bitfield & 0x1F ? decoded->bitfield & 0x1F : 32); case 0x20: return ret + sprintf(dst+ret, " {$%X:d%d}", decoded->bitfield >> 6 & 0x1F, decoded->bitfield & 0x7); case 0x800: return ret + sprintf(dst+ret, " {d%d:%d}", decoded->bitfield >> 6 & 0x7, decoded->bitfield & 0x1F ? decoded->bitfield & 0x1F : 32); case 0x820: return ret + sprintf(dst+ret, " {d%d:d%d}", decoded->bitfield >> 6 & 0x7, decoded->bitfield & 0x7); } } #endif return ret; } int m68k_disasm_movem_op(m68k_op_info *decoded, m68k_op_info *other, char *dst, int need_comma, uint8_t labels, uint32_t address, format_label_fun label_fun, void * data) { int8_t dir, reg, bit, regnum, last=-1, lastreg, first=-1; char *rtype, *last_rtype; int oplen; if (decoded->addr_mode == MODE_REG) { if (other->addr_mode == MODE_AREG_PREDEC) { bit = 15; dir = -1; } else { dir = 1; bit = 0; } if (need_comma) { strcat(dst, ", "); oplen = 2; } else { strcat(dst, " "); oplen = 1; } for (reg=0; bit < 16 && bit > -1; bit += dir, reg++) { if (decoded->params.immed & (1 << bit)) { if (reg > 7) { rtype = "a"; regnum = reg - 8; } else { rtype = "d"; regnum = reg; } if (last >= 0 && last == regnum - 1 && lastreg == reg - 1) { last = regnum; lastreg = reg; } else if(last >= 0) { if (first != last) { oplen += sprintf(dst + oplen, "-%s%d/%s%d",last_rtype, last, rtype, regnum); } else { oplen += sprintf(dst + oplen, "/%s%d", rtype, regnum); } first = last = regnum; last_rtype = rtype; lastreg = reg; } else { oplen += sprintf(dst + oplen, "%s%d", rtype, regnum); first = last = regnum; last_rtype = rtype; lastreg = reg; } } } if (last >= 0 && last != first) { oplen += sprintf(dst + oplen, "-%s%d", last_rtype, last); } return oplen; } else { return m68k_disasm_op(decoded, dst, need_comma, labels, address, label_fun, data); } } int m68k_default_label_fun(char * dst, uint32_t address, void * data) { return sprintf(dst, "ADR_%X", address); } int m68k_disasm_ex(m68kinst * decoded, char * dst, uint8_t labels, format_label_fun label_fun, void * data) { int ret,op1len; uint8_t size; char * special_op = "CCR"; switch (decoded->op) { case M68K_BCC: case M68K_DBCC: case M68K_SCC: ret = strlen(mnemonics[decoded->op]) - 2; memcpy(dst, mnemonics[decoded->op], ret); dst[ret] = 0; strcpy(dst+ret, cond_mnem[decoded->extra.cond]); ret = strlen(dst); if (decoded->op != M68K_SCC) { if (labels) { if (decoded->op == M68K_DBCC) { ret += sprintf(dst+ret, " d%d, ", decoded->dst.params.regs.pri); ret += label_fun(dst+ret, decoded->address + 2 + decoded->src.params.immed, data); } else { dst[ret++] = ' '; ret += label_fun(dst+ret, decoded->address + 2 + decoded->src.params.immed, data); } } else { if (decoded->op == M68K_DBCC) { ret += sprintf(dst+ret, " d%d, #%d <%X>", decoded->dst.params.regs.pri, decoded->src.params.immed, decoded->address + 2 + decoded->src.params.immed); } else { ret += sprintf(dst+ret, " #%d <%X>", decoded->src.params.immed, decoded->address + 2 + decoded->src.params.immed); } } return ret; } break; case M68K_BSR: if (labels) { ret = sprintf(dst, "bsr%s ", decoded->variant == VAR_BYTE ? ".s" : ""); ret += label_fun(dst+ret, decoded->address + 2 + decoded->src.params.immed, data); } else { ret = sprintf(dst, "bsr%s #%d <%X>", decoded->variant == VAR_BYTE ? ".s" : "", decoded->src.params.immed, decoded->address + 2 + decoded->src.params.immed); } return ret; case M68K_MOVE_FROM_SR: ret = sprintf(dst, "%s", mnemonics[decoded->op]); ret += sprintf(dst + ret, " SR"); ret += m68k_disasm_op(&(decoded->dst), dst + ret, 1, labels, decoded->address, label_fun, data); return ret; case M68K_ANDI_SR: case M68K_EORI_SR: case M68K_MOVE_SR: case M68K_ORI_SR: special_op = "SR"; case M68K_ANDI_CCR: case M68K_EORI_CCR: case M68K_MOVE_CCR: case M68K_ORI_CCR: ret = sprintf(dst, "%s", mnemonics[decoded->op]); ret += m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address, label_fun, data); ret += sprintf(dst + ret, ", %s", special_op); return ret; case M68K_MOVE_USP: ret = sprintf(dst, "%s", mnemonics[decoded->op]); if (decoded->src.addr_mode != MODE_UNUSED) { ret += m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address, label_fun, data); ret += sprintf(dst + ret, ", USP"); } else { ret += sprintf(dst + ret, "USP, "); ret += m68k_disasm_op(&(decoded->dst), dst + ret, 0, labels, decoded->address, label_fun, data); } return ret; case M68K_INVALID: ret = sprintf(dst, "dc.w $%X", decoded->src.params.immed); return ret; #ifdef M68010 case M68K_MOVEC: ret = sprintf(dst, "%s ", mnemonics[decoded->op]); if (decoded->src.addr_mode == MODE_UNUSED) { ret += sprintf(dst + ret, "%s, ", cr_mnem[decoded->src.params.immed]); ret += m68k_disasm_op(&(decoded->dst), dst + ret, 0, labels, decoded->address, label_fun, data); } else { ret += m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address, label_fun, data); ret += sprintf(dst + ret, ", %s", cr_mnem[decoded->dst.params.immed]); } return ret; #endif default: size = decoded->extra.size; uint8_t is_quick = decoded->variant == VAR_QUICK && decoded->op != M68K_ASL && decoded->op != M68K_ASR && decoded->op != M68K_LSL && decoded->op != M68K_LSR && decoded->op != M68K_ROXR && decoded->op != M68K_ROXL && decoded->op != M68K_ROR && decoded->op != M68K_ROL; ret = sprintf(dst, "%s%s%s", mnemonics[decoded->op], is_quick ? "q" : (decoded->variant == VAR_IMMEDIATE ? "i" : ""), size == OPSIZE_BYTE ? ".b" : (size == OPSIZE_WORD ? ".w" : (size == OPSIZE_LONG ? ".l" : ""))); } if (decoded->op == M68K_MOVEM) { op1len = m68k_disasm_movem_op(&(decoded->src), &(decoded->dst), dst + ret, 0, labels, decoded->address, label_fun, data); ret += op1len; ret += m68k_disasm_movem_op(&(decoded->dst), &(decoded->src), dst + ret, op1len, labels, decoded->address, label_fun, data); } else { op1len = m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address, label_fun, data); ret += op1len; ret += m68k_disasm_op(&(decoded->dst), dst + ret, op1len, labels, decoded->address, label_fun, data); } return ret; } int m68k_disasm(m68kinst * decoded, char * dst) { return m68k_disasm_ex(decoded, dst, 0, NULL, NULL); } int m68k_disasm_labels(m68kinst * decoded, char * dst, format_label_fun label_fun, void * data) { if (!label_fun) { label_fun = m68k_default_label_fun; } return m68k_disasm_ex(decoded, dst, 1, label_fun, data); } blastem-0.6.3.4/68kinst.h000066400000000000000000000131351374760425700150120ustar00rootroot00000000000000/* Copyright 2013 Michael Pavone This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #ifndef M68KINST_H_ #define M68KINST_H_ #include #ifdef M68030 #define M68020 #endif #ifdef M68020 #define M68010 #endif typedef enum { BIT_MOVEP_IMMED = 0, MOVE_BYTE, MOVE_LONG, MOVE_WORD, MISC, QUICK_ARITH_LOOP, BRANCH, MOVEQ, OR_DIV_SBCD, SUB_SUBX, A_LINE, CMP_XOR, AND_MUL_ABCD_EXG, ADD_ADDX, SHIFT_ROTATE, F_LINE } m68k_optypes; typedef enum { M68K_ABCD, M68K_ADD, M68K_ADDX, M68K_AND, M68K_ANDI_CCR, M68K_ANDI_SR, M68K_ASL, M68K_ASR, M68K_BCC, M68K_BCHG, M68K_BCLR, M68K_BSET, M68K_BSR, M68K_BTST, M68K_CHK, M68K_CLR, M68K_CMP, M68K_DBCC, M68K_DIVS, M68K_DIVU, M68K_EOR, M68K_EORI_CCR, M68K_EORI_SR, M68K_EXG, M68K_EXT, M68K_ILLEGAL, M68K_JMP, M68K_JSR, M68K_LEA, M68K_LINK, M68K_LSL, M68K_LSR, M68K_MOVE, M68K_MOVE_CCR, M68K_MOVE_FROM_SR, M68K_MOVE_SR, M68K_MOVE_USP, M68K_MOVEM, M68K_MOVEP, M68K_MULS, M68K_MULU, M68K_NBCD, M68K_NEG, M68K_NEGX, M68K_NOP, M68K_NOT, M68K_OR, M68K_ORI_CCR, M68K_ORI_SR, M68K_PEA, M68K_RESET, M68K_ROL, M68K_ROR, M68K_ROXL, M68K_ROXR, M68K_RTE, M68K_RTR, M68K_RTS, M68K_SBCD, M68K_SCC, M68K_STOP, M68K_SUB, M68K_SUBX, M68K_SWAP, M68K_TAS, M68K_TRAP, M68K_TRAPV, M68K_TST, M68K_UNLK, M68K_INVALID, M68K_A_LINE_TRAP, M68K_F_LINE_TRAP, #ifdef M68010 M68K_BKPT, M68K_MOVE_FROM_CCR, M68K_MOVEC, M68K_MOVES, M68K_RTD, #endif #ifdef M68020 M68K_BFCHG, M68K_BFCLR, M68K_BFEXTS, M68K_BFEXTU, M68K_BFFFO, M68K_BFINS, M68K_BFSET, M68K_BFTST, M68K_CALLM, M68K_CAS, M68K_CAS2, M68K_CHK2, M68K_CMP2, M68K_CP_BCC, M68K_CP_DBCC, M68K_CP_GEN, M68K_CP_RESTORE, M68K_CP_SAVE, M68K_CP_SCC, M68K_CP_TRAPCC, M68K_DIVSL, M68K_DIVUL, M68K_EXTB, M68K_PACK, M68K_RTM, M68K_TRAPCC, M68K_UNPK, #endif } m68K_op; typedef enum { VAR_NORMAL, VAR_QUICK, VAR_IMMEDIATE, VAR_BYTE, VAR_WORD, VAR_LONG } m68K_variant; typedef enum { OPSIZE_BYTE=0, OPSIZE_WORD, OPSIZE_LONG, OPSIZE_INVALID, OPSIZE_UNSIZED } m68K_opsizes; typedef enum { //actual addressing mode field values MODE_REG = 0, MODE_AREG, MODE_AREG_INDIRECT, MODE_AREG_POSTINC, MODE_AREG_PREDEC, MODE_AREG_DISPLACE, MODE_AREG_INDEX_MEM, //bunch of relatively complicated modes MODE_PC_INDIRECT_ABS_IMMED, //Modes that use the program counter, an absolute address or immediate value //expanded values MODE_AREG_INDEX_DISP8, #ifdef M68020 MODE_AREG_INDEX_BASE_DISP, MODE_AREG_PREINDEX, MODE_AREG_POSTINDEX, MODE_AREG_MEM_INDIRECT, MODE_AREG_BASE_DISP, MODE_INDEX_BASE_DISP, MODE_PREINDEX, MODE_POSTINDEX, MODE_MEM_INDIRECT, MODE_BASE_DISP, #endif MODE_ABSOLUTE_SHORT, MODE_ABSOLUTE, MODE_PC_DISPLACE, MODE_PC_INDEX_DISP8, #ifdef M68020 MODE_PC_INDEX_BASE_DISP, MODE_PC_PREINDEX, MODE_PC_POSTINDEX, MODE_PC_MEM_INDIRECT, MODE_PC_BASE_DISP, MODE_ZPC_INDEX_BASE_DISP, MODE_ZPC_PREINDEX, MODE_ZPC_POSTINDEX, MODE_ZPC_MEM_INDIRECT, MODE_ZPC_BASE_DISP, #endif MODE_IMMEDIATE, MODE_IMMEDIATE_WORD,//used to indicate an immediate operand that only uses a single extension word even for a long operation MODE_UNUSED } m68k_addr_modes; #ifdef M68020 #define M68K_FLAG_BITFIELD 0x80 #endif typedef enum { COND_TRUE, COND_FALSE, COND_HIGH, COND_LOW_SAME, COND_CARRY_CLR, COND_CARRY_SET, COND_NOT_EQ, COND_EQ, COND_OVERF_CLR, COND_OVERF_SET, COND_PLUS, COND_MINUS, COND_GREATER_EQ, COND_LESS, COND_GREATER, COND_LESS_EQ } m68K_condition; #ifdef M68010 typedef enum { CR_SFC, CR_DFC, #ifdef M68020 CR_CACR, #endif CR_USP, CR_VBR, #ifdef M68020 CR_CAAR, CR_MSP, CR_ISP #endif } m68k_control_reg; #ifdef M68020 #define MAX_HIGH_CR 0x804 #define MAX_LOW_CR 0x002 #else #define MAX_HIGH_CR 0x801 #define MAX_LOW_CR 0x001 #endif #endif typedef struct { #ifdef M68020 uint16_t bitfield; #endif uint8_t addr_mode; union { struct { uint8_t pri; uint8_t sec; #ifdef M68020 uint8_t scale; uint8_t disp_sizes; #endif int32_t displacement; #ifdef M68020 int32_t outer_disp; #endif } regs; uint32_t immed; } params; } m68k_op_info; typedef struct m68kinst { uint8_t op; uint8_t variant; union { uint8_t size; uint8_t cond; } extra; uint8_t bytes; uint32_t address; m68k_op_info src; m68k_op_info dst; } m68kinst; typedef enum { VECTOR_RESET_STACK, VECTOR_RESET_PC, VECTOR_ACCESS_FAULT, VECTOR_ADDRESS_ERROR, VECTOR_ILLEGAL_INST, VECTOR_INT_DIV_ZERO, VECTOR_CHK, VECTOR_TRAPV, VECTOR_PRIV_VIOLATION, VECTOR_TRACE, VECTOR_LINE_1010, VECTOR_LINE_1111, VECTOR_COPROC_VIOLATION=13, VECTOR_FORMAT_ERROR, VECTOR_UNINIT_INTERRUPT, VECTOR_SPURIOUS_INTERRUPT=24, VECTOR_INT_1, VECTOR_INT_2, VECTOR_INT_3, VECTOR_INT_4, VECTOR_INT_5, VECTOR_INT_6, VECTOR_INT_7, VECTOR_TRAP_0, VECTOR_TRAP_1, VECTOR_TRAP_2, VECTOR_TRAP_3, VECTOR_TRAP_4, VECTOR_TRAP_5, VECTOR_TRAP_6, VECTOR_TRAP_7, VECTOR_TRAP_8, VECTOR_TRAP_9, VECTOR_TRAP_10, VECTOR_TRAP_11, VECTOR_TRAP_12, VECTOR_TRAP_13, VECTOR_TRAP_14, VECTOR_TRAP_15, VECTOR_USER0 = 64 } m68k_vector; typedef int (*format_label_fun)(char * dst, uint32_t address, void * data); uint16_t * m68k_decode(uint16_t * istream, m68kinst * dst, uint32_t address); uint32_t m68k_branch_target(m68kinst * inst, uint32_t *dregs, uint32_t *aregs); uint8_t m68k_is_branch(m68kinst * inst); uint8_t m68k_is_noncall_branch(m68kinst * inst); int m68k_disasm(m68kinst * decoded, char * dst); int m68k_disasm_labels(m68kinst * decoded, char * dst, format_label_fun label_fun, void * data); int m68k_default_label_fun(char * dst, uint32_t address, void * data); #endif blastem-0.6.3.4/Android.mk000066400000000000000000000022431374760425700152420ustar00rootroot00000000000000LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := main SDL_PATH := android/jni/SDL LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include LOCAL_CFLAGS += -std=gnu99 -DX86_32 -DUSE_GLES # Add your application source files here... LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \ 68kinst.c debug.c gst.c psg.c z80_to_x86.c backend.c io.c render_sdl.c \ tern.c backend_x86.c gdb_remote.c m68k_core.c romdb.c m68k_core_x86.c \ util.c wave.c blastem.c gen.c mem.c vdp.c ym2612.c config.c gen_x86.c \ terminal.c z80inst.c menu.c arena.c zlib/adler32.c zlib/compress.c \ zlib/crc32.c zlib/deflate.c zlib/gzclose.c zlib/gzlib.c zlib/gzread.c \ zlib/gzwrite.c zlib/infback.c zlib/inffast.c zlib/inflate.c \ zlib/inftrees.c zlib/trees.c zlib/uncompr.c zlib/zutil.c \ nuklear_ui/font_android.c nuklear_ui/blastem_nuklear.c nuklear_ui/sfnt.c \ ppm.c controller_info.c png.c system.c genesis.c sms.c serialize.c \ saves.c hash.c xband.c zip.c bindings.c jcart.c paths.c megawifi.c \ nor.c i2c.c sega_mapper.c realtec.c multi_game.c net.c LOCAL_SHARED_LIBRARIES := SDL2 LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog include $(BUILD_SHARED_LIBRARY) blastem-0.6.3.4/CHANGELOG000066400000000000000000000712651374760425700145550ustar00rootroot000000000000000.6.3.4 (pre) * Add support for the parts of the KMod debug ports used by SGDK default tip. * Fix build breakage on OS X. * Specify min OS X version when making a "portable" build for that platform. * Prevent wait truncation in VGM logging. * Set initial pan bits in YM2612 register array and not just the separate lr field of the channel. This fixes an issue in which some channels would be silent in VGM log output. * Fix occasional deadlock on startup when using audio sync. * Added Wii U controller image. * Fix VGM delay calculation overflow when a YM-2612 write follows a PSG write in close succession. * Add stubs for some functions in libblastem.c to fix link failures on windows libretro target. * Fix broken enum definitions that cause multiple definition errors when building with -fno-common which is now the default in GCC 10. * Fix libretro target on windows. * Enter debugger when a VDP data port read would cause a CPU lockup. * Fix regressions from most recent address/cd latch change. Need to do more research still, but probably good enough for now. * Update cycle to VGM sample conversion based on ValleyBell's suggestion. * Fix regressions in Monster World IV and Sonic 3D Blast caused by address latch changes. * Add stop command to end of recorded VGM stream. * Somewhat buggy implementations of shift instructions in new 68K core. * Fix 68k test harness target, add cycle count to output and add a cycle limit. * Fix cycle timing of a number of 68K instructions. * Add ROM DB entry for Sonic Delta. * Fix handling of unmapped reads/writes to the cart/expansion port region. * Fix libretro target. * Fix vgmplay target. * Fix merge error mame_interp. * Merge from default mame_interp. * Fix regression in Mode 4 support caused by address/cd latch changes. * Go back to unpausing audio in render_video_loop to ensure the core is no longer running on the main thread when audio callbacks start when using run on audio thread mode. * Less hacky run on audio thread mode. * Don't hold frame queue mutex while rendering. * Fix regression in run on audio thread mode. * Allow use of NPOT textures as a config option. Useful for some mobile GPUs. * Specify desired language when invoking fc-match to find an appropriate font on Linux. * Avoid expensive re-init from switching to external sync after render_init has been called. * Better handling of pad assignment to remotes. * Fix handling of remote disconnects. * Fix "full" deflate flush so multiple remotes can successfully join. * Add an event log soft flush and call it twice per frame in between hard flushes to netplay latency when there are insufficient hardware updates to flush packets in the middle of a frame. * megawifi: implement CMD_DATETIME. * megawifi: implement UDP sockets. * megawifi: use util module socket functions for WIN32 compatibility. * Make netplay remote sync to network rather than audio or video so it doesn't drift out of sync with the host. * Skip setting AI_NUMERICSERV in megawifi code on Windows for now so min SDK version does not need to be bumped. * More correct implementation of byte printing in builtin debugger. Fix GDB debugger to use helper in backend.c for reading bytes. * Apply fixes to helper functions in backend.c from interp branch. * Add support for printing a byte from memory in native debugger. Add stubs for GDB commands qThreadExtraInfo and qP. * Implement CMD_HRNG_GET, CMD_GAMERTAG_GET and CMD_LOG. * Implement CMD_TCP_CON command. * megawifi: stub common commands to get config. * megawifi: refactor and update commands. * Use zlib to compress event log streams. * Fix some netplay issues. * Netplay protocol size optimization. * Fix awful playback latencin in new netplay implementation. * Properly initialize Genesis reset cycle on startup. Fixes crash in GDB remote debugger when stepping past the first two instructions. * Fix some questionable comparisons between 64-bit values and literals that fit in 32-bit integers. * Remove usage of GCC pointer arithmetic on void * extension. * Add missing file from new 68K core. * Fix non-Windows build break from Windows compat changes. * Get WIP net play code compiling on Windows and cleanup some unistd.h includes. * Fix short event format decode bug. * Add missing netplay files and add in support for sending gamepad commands back to host. * WIP netplay support. * Fix addrinfo leak in GDB remote debug support. * Fix vgmplay target. * Kill the stateview target as it is not very useful these days and breaks a lot. * Fix instruction timing for addq.w #i, (ay) in dynarec. * Implement 68K or and sub instructions in new core. * Implement 68K eor instruction in new core. * Implement 68K and instruction in new core. * Fix autogenerated temp variables in interrupt subroutine in CPU DSL. * Fix resuming 68K core when using interpreter mame_interp. * Merge from default mame_interp. * Allow override of SDL2 include path for "portable" builds. * Allow specifying a default font path at build time. * Make requested sample format configurable. * WIP new sync mode that runs emulation on audio thread. * Merge from default mame_interp. * Fix stateview target. * Fix regression in Konami games from address/cd latch changes. * Admit defeat on the "trying to write CRAM dots while output is null issue" for now and just add a null check. * Update libretro target to use render_audio shared audio code. * Add memory hook for genesis/megadrive save and cheevos support. * Don't apply address and cd register changes to the 'live' registers until pending flag is cleared, but do preserve the upper address bits in the latch. Fixes regression in Overdrive 2 while preserving fix to Mona in 344 bytes. * Add memory hook for cheat/rumble/cheevos support. * Added implementation of printing PC in 68K debugger. * Added soft reset command to debugger. Added more debugger help. * Added help commands to debugger. * Use proper memory map in Z80 debugger for memory printing. * Z80 HALT isn't really terminal. Fixes bug in Z80 disassembler. * Fix bug in in (c) instruction in Z80 dynarec. * Top bits of address register should be cleared on partial command word write. Fixes Mona in 344 bytes demo. * Make sure fallback memory region is always last. * Don't lockup on writes to !TIME or !FDC regions regardless of whether anything is mapped there. * Reading from Z80 bus when Z80 is not bus requested should return open bus. Fixes regression in Metal Sonic Rebooted. * Fix regression at the very start of The Revenge of Shinobi. * Skip invalid registers when dumping initial YM2612 state to VGM log. * Fix PSG frequency written to VGM header when logging. * Expose vgm toggle keybind in settings UI. * Initial stab at VGM logging support. * Fix stateview target. * Some partial work on TMSS registers, more accurate open bus locations and implement machine freezes for unmapped areas in the IO region. * Make VDP VSRAM capacity respect model selection. * Fix fm setting for Model 3 VA2. * Implement selectable YM2612/YM3834 invalid status port behavior. * Fix edge case in Z80 interrupt handling. Fixes music in Metal Blast 2277. * Fix YM2612 busy flag timing. * Set version reg and TAS behavior based on model config. * Added UI for selecting configured model. * Fix crash in OD2 Titancade scene when border is completely cropped by overscan settings. * Make sure save_type is properly initialized. * Fix crash in 68K debugger from forced VDP frame update when framebuffer is not acquired. * Reset 68K supervisor state and interrupt mask on soft reset. * Fix debug view window stuff that got broken when FRAMEBUFFER_UI got added. * Fix regression in handling of color index 0 in Mode 4. Support Mode 4 in CRAM viewer window. * Only look at low 24-bits of reset vector in ROM type detection heuristic. * Properly mask addresses to 24-bit in disassembler. * Wait to reacquire framebuffer so that switching to UI does not require pushing a new frame if it happens in between bottom and top of display. * Hopefully final fix for line advancement/frame end calculation. * Report more accurate frame and sample rates to frontend in libretro target. * Fix regression in H32 from fine scroll optimization. * Small optimization to read_map_scroll. * Optimized sprite rendering. * Calculate fine scroll once per line for a small speedup. * Slightly gross fix for edge case introduced in border cropping change. * Implement interrupts in call dispatch mode in CPU DSL. * Implement overscan crop in libretro target. * Don't render lines that are cropped by overscan. Allows submitting frame earlier when bottom overscan is non-zero which can reduce latency in some cases. * Cache operator phase increment for a small perf improvement. * Split ym_run into a few different functions to enhance clarity. * Small optimization to render_normal and a minor bugfix in left border debug register handling. * Fix sprite rendering regression introduced by H40 line at a time optimization. * Only do full sync on VDP data port reads instead of all VDP port reads, provides a perf bump for games that busy wait on the status or HV registers. * Small optimization to render_map in VDP code. * Draw entire lines in H40 mode when possible. Still seems to have an edge case or two, but mostly working well. * Fix regression in sprite rendering in H32 mode. * Forgot to commit the header changes. 0.6.3.3 (pre) * Fix debug register output regression in border region * Forgot to commit the header changes 0.6.3.2 (pre) ------------- * Properly handle freeing a paused audio source. Fixes crash when repeatedly reloading a ROM or loading a sequence of different ROMs. 0.6.3.1 (pre) ------------- * Differentiate between the full Sega mapper and the SRAM only one * Split generic part of audio code into a separate file so it can be used in other targets besides SDL * Add input descriptors for remapping from the 'controls' menu * Rework sprite rendering phase 3 to better match behavior documented by Kabuto/Titan and fix edge case in sprite overflow flag that was breaking the RPS minigame in Alex Kidd * Fix libretro and stateview targets * Fix accuracy bugs used by Novedicus to detect BlastEm/Exodus 0.6.2.1 (pre) ------------- * Added icon in PNG format * Fixed warnings in compilation - vgmplay.c * Resolution of the changed window and the nearest scale * Manpage created with basic commands * Added readme.md with basic emulator summary * Added desktop file * Fixed test.c file * Added -g in GCC to appear debug information during compilation * Spelling errors fixed in file * Removed the hgignore hidden file * Disabled warning Wincompatible-pointer-types * Makefile: Make pkg-config substitutable * Spelling errors fixed in file * Fixed the FTBFS due to enabled hardening build flags for packaging * Makefile: Commented line: error $(CPU) is not a supported architecture 0.6.2 ----- *New Features* - Zipped and gzipped SMD ROMs are now supported - Gain control for overall volume and FM/PSG invidually *Accuracy/Completeness Improvements* - Fixed timing of a few instructions in Z80 core - Added optional emulation of YM2612 imperfections (aka "ladder effect") - Fixed some unintentional extra precision in some FM LFO calculations - Added a 1 sample delay in some FM operator results when used as modulators to match hardware *Bugfixes* - Fixed regression in NBA JAM TE and possibly other 32MBit Acclaim mapper titles - Added code to handle controllers that have their d-pads mapped as buttons or axes - Removed some problematic SDL2 game controller mappings - Fixed crash that occurred when releasing mouse too clickly when loading a ROM - Fixed SMD ROM support - Fixed handling of audio contexts with more or less than 2 channels - Fixed off-by-one error in IO device selection UI - Fixed regression in GDB remote debugging support on Linux and OS X *Other Changes* - MegaWiFi hardware can now be enabled by a header string (still gated by config) - Tweaked the style of checkboxes in the Nuklear UI to hopefully make the on/off state more clear 0.6.1 ----- *Bugfixes* - Fixed build script so controller images are actually included so UI doesn't crash - Disabled most bindings when UI active (fixes crashes/wonkiness when pressing certain keys) - Fixed Windows implementation of get_config_dir() so config file ends up in %localappdata%\blastem like it should - Fixed the location of sticky_path on all platforms - Added virtual root directory used by ROM UI to new Nuklear UI 0.6.0 ----- *New Features* - New Nuklear UI with almost complete access to configuration options - Added support for creating SDL2 mappings from inside the emulator - Loading ROMs from ZIP archives is now supported - Loading gzip compressed ROMs is now supported - Internal screenshots can now be in PNG format - New VDP plane debug view - VDP debug views are now in separate windows - Experimental support for sync to video (not enabled by default) - Preliminary support for MegaWifi cart emulation *Bugfixes* - Fixed a number of 68K decoder bugs (mostly illegal instruction decoding) - Fixed implementation of the UNLK instruction when register is a7 - Fixed a number of memory leaks *Accuracy/Completeness Improvements* - Added support for J-Cart emulation - Implemented Z80 interrupt mode 2 - Fixed the timing of a number of 68K instructions - Fixed the timing of the Z80 JP cc, nn instruction - Fixed the order bytes of a word are written to VRAM from the FIFO (fixes minor corruption in Road Rash 3 bike menu) *Other Changes* - Added support for Open GL ES in addition to the existing desktop GL support - Some small optimizations - Added ROM DB entry for Squirrel King to support it's copy protection - Added support for float32 audio output (fixes an issue with default SDL2 driver in Windows when using more recent SDL2 versions) 0.5.1 ----- *New Features* - Drag and Drop is now supported for loading ROMs - Save states are now supported for SMS games - Texture scaling method (linear or nearest neighbor) can now be selected in both renderers - Menu now filters files based on a configurable extension list - Lock on carts (Sonic & Knuckles and XBAND) can now be loaded via the menu - ROMs can be reloaded via a hotkey (defaults to F5) - Last path visited in the menu is now saved between runs (can be turned off via config) - Window height can now be specified in the config file *Bugfixes* - Default shader doesn't look like garbage in interlaced mode anymore - Framebuffer pointers are properly released and reacquired on context switch (no more LOCKRECT errors) - ROMs specifying SRAM at the normal RAM address no longer cause a crash - Fixed an edge case in the s(tep) debugger command - Entering the option menu in Dragon's Fury no longer results in a fatal error in 32-bit builds - Screen is properly cleared so garbage will not appear when the window does not match the emulated display size - Fixed a regression in XBAND keyboard support *Accuracy/Completeness Improvements* - Locking on Sonic 3 to S&K will now use Sonic 3's save RAM - Locking on a 4MB cart to S&K will now behave like on hardware - Support for several X-in-1 bootleg carts has been added - DMA from byte-wide SRAM now yields correct results - VScroll is now latched earlier in the line (fixes minor glithces in Top Gear 2 and Skitchin) - Sega/SSF2 mapper support now handles homebrew that uses SRAM - ODD flag timing now matches hardware - V counter as read from HV port is now correct in single-resolution interlace mode *Other Changes* - Added a "subtle" CRT shader contributed by Anaël Seghezzi - Mouse is now only captured if an emulated mouse is plugged in to the emulated system - Missing mapping warnings will only be displayed for the first mapping of a controller - Save states now default to a format native to BlastEm - Remaining I2C EEPROM games have been added to the ROM DB - When not specified, height now respects the aspect setting rather than assuming 4:3 - Pre-combined S&K ROMs and large (>2MB) S&K hacks should now work - Using ui.exit (default Escape) can now be used to cleanly cancel a load ROM or savestate action - Save states are now allowed in a more extensive range of Z80 states, fixing save state saving in some games 0.5.0 ----- *New Features* - SMS emulation in the form of the Gensis/MD's backwards compatibility mode - Added support for SMS controllers - Support for the mapper used by Realtec games - Support for carts with fixed value registers - Support for enough of the XBAND cartridge hardware to allow the menu to boot - Basic XBAND keyboard emulation - Configurable display overscan - Fullscreen mode can now be toggled at runtime - Window can now be resized at runtime - Support for "semantic" controller button names in the gamepad mapping using SDL2's game controller API - Analog axes can now be mapped to emulated gamepad buttons or UI actions - System soft reset - Keyboard can now be captured when a Saturn or XBAND keyboard is connected to the emulated system - Internal screenshots that bypass all output filtering/overscan - Homebrew using the "SSF2 Mapper" is now supported via header detection like on the Mega Everdrive - Directory used for SRAM, EEPROM and savestates is now configurable - Path configuration values can now contain both BlastEm-specific and environment variable references - Open GL based rendering can be disabled in favor of the SDL2 render API fallback *Bugfixes* - Fixed a bug that would cause a crash in certain games that have SRAM and ROM at the same address - Fixed some issues with Z80 interrupts that caused issues with sound/music in Sonic 2 and Puyo Puyo 2 - Z80 debugger no longer crashes when resuming execution - Undocumented Z80 instruction "out (c), 0" now decodes properly - GDB remote debugging should now work with more recent versions of GDB - GDB remote debugging should now work on more recent versions of Windows - Overlapping instructions in self-modifying code no longer causes incorrect behavior - Z80 instructions "in c" and "out c" now work correclty on 32-bit builds - Specifying an output audio frequency higher than the FM frequency no longer deadlocks the emulator - Fixed memory map generation for games with 3MB ROM and SRAM at the 3MB mark *Accuracy/Completeness Improvements* - YM2612 SSG-EG and CSM modes are now implemented - VDP Mode 4 is now implemented in both Genesis and SMS mode - Basic emulation of refresh delays has been added - 68K interrupt latency has been made more accurate - CRAM contention artifacts (aka CRAM dots) are now emulated - DIVU/DIVS and MULU/MULS are now cycle accurate - MOVEM now performs the extra ignored read and has correct timing - The timing of serveral other 68K instructions has been fixed - Implemented 68K trace mode - SBCD flag calculation now matches hardware in 100% of cases - 68K -> VDP DMA now properly has a delay at DMA start rather than at the end of the transfer - A number of illegal effective address mode/operation combinations now properly decode as illegal instructions - Added emulation of the slow rise time of an IO pin that was changed to an input when it was previously outputting 0 - Partial support for the VDP test register - Partial support for the 128KB VRAM mode bit - Improved accuracy of low level sprite rendering details - Fixed handling of active/passive display transitions so that border extension tricks work - Fixed handling of horizontal interrupts in extended display areas - More accurate correspondance between horizontal counter and raster beam - Partial emulation of serial IO registers *Other Changes* - Added Japanese version of Street Fighter 2: The New Challengers to ROM DB - Added the following EEPROM games to ROM DB: Ninja Burai Densetsu Rockman Mega World - Added ROM DB entries for the following games with incorrect region headers: Another World (E) Alien Soldier (J) Light Crusader (J) Castle of Illusion - Fushigi no Oshiro Daibouken (J) Atomic Robo-Kid (J) - Added ROM DB entries for the following games which are incompatible with 6-button controllers: King of Monsters Combat Cars Second Samurai Dungeons & Dragons - Warriors of the Eternal Sun - Added ROM DB entries for the following games with fixed value registers: Ya Se Chuan Sho 16 Zhang Ma Jiang Elf Wor Huan Le Tao Qi Shu: Smart Mouse Mighty Morphin' Power Rangers: The Fighting Edition Super Bubble Bobble MD Thunderbolt II - Added ROM DB entries for the following games that have bad/missing SRAM headers: Hardball III Might and Magic - Gates to Another World Might and Magic III - Isles of Terra 0.4.1 ----- *New Features* - Basic support for the Saturn Keyboard adapter peripheral - You can now navigate up to a drive selection on Windows - Added support for binding more "special" keys *Bugfixes* - It's now possible to navigate to the root directory on Unix-like systems - Fixed a bug in movep.l - Fixed a crash bug in the memory management code - Fixed a bug in the header parsing code, that caused a crash when the "International Name" field was empty - Fixed some minor graphical corruption in Sonic 2 split-screen caused by a bug in vflip combined with interlace mode - Corrected the PC value pushed onto the stack for line A emulator and line F emulator traps - Fixed a bug in ensure_dir_exists that would cause it to fail to create directories in some cases when mixed path separators were used - Fixed a bug that would result in a buffer overflow when starting a game with a long title from the menu *Accuracy/Completeness Improvements* - All Z80 instructions are now implemented - Z80 Half-carry flag is now fully implemented - Implemented undocumented Z80 flag bits - R register is now incremented appropriately - Redundant opcode prefixes are now handled properly - Z80 core now passes ZEXALL! *Other Changes* - Added Buck Rogers to the ROM DB - Added Wonder Boy in Monster World to the ROM DB (for real this time) - Added Evander 'Real Deal' Holyfield's Boxing to the ROM DB - Slightly better handling of directory read errors - Added "Special Thanks" to About menu - Use local app data folder for saves and config files on Windows rather than Unix-style locations 0.4.0 ----- *New Features* - Genesis ROM based Graphical User Interface - command line is no longer required for basic functionality - Added support for the Mega/Sega Mouse - Configurable low pass filter - 68000 overclock and underclock - Scanlines can now be controlled via the config file and defaults to off (previously was always on) - VSync can now be specified via the config file and defaults to off (previously just used the OS/driver default) - Fullscreen mode can now be specified via the config file in addition to the command line flag - New 68K debugger command 'co' allows a list of commands to be run each time a breakpoint is hit - 68K debugger now supports the 'di' command like the Z80 debugger - New debugger command 'yt' displays YM-2612 timer info - Added support for controller hotplug (game controllers don't need to be plugged in before starting BlastEm) - IO devices can now be automatically configured by ROM DB entries *Bugfixes* - Fixed calculation of window start column - removes graphical glitches in Afterburner 2, Fireshark, Dungeons and Dragons: Warriors of the Eternal Sun and probably others - Fixed the implementation of LDD and LDDR - Fixed ABCD/SBCD - eliminates the score counter problem in Bubsy - Fixed btst when used with immediate destination - Fixes a crash in NHL 95 - Fixed YM-2612 attack phase and sustain level - Fixed mapping of YM-2612 key on/off bits to operators - Fixed YM-2612 LFO AMS shift values - Fixed YM-2612 LFO phase modulation - Fixed mapping of registers to operators in Channel 3 special mode (for real this time) - Fixed a small bug in YM-2612 timer reloads - Fixed peripheral ID for 3-button pad - Accesses by the 68K to the low 4MB of the address space, but outside of the defined ROM will no longer cause a crash - Config files and shaders saved with Windows-style line endings will no longer fail to load - Fixed a crash bug on Windows by properly detecting invalid destination modes for immediate variant opcodes *Accuracy/Completeness Improvements* - All 68000 instructions are now implemented - Implemented 68000 privilege, address error and illegal instruction exceptions - Z80 half carry flag is now implemented for the trivial cases - Fixed timing of the Z80 IM and certain LD variants - Implemented interrupt latency - Fixes Sesame Street: Counting Cafe - Interrupts are know acknowleged based on what the VDP thinks its asserting rather than what the 68K actually is acking - Fixes Fatal Rewind - Improved timing of 68000 interrupt processing - Improved timing of Z80 busack response - Fixes a crash in Barkley: Shut Up and Jam - Adjusted the amount of time the 68000 is blocked during DMA - gets rid of remaining part of "YOUR EMULATOR SUX" text in overdrive - Corrected order individual words of a longword are written when the predecrement addressing mode is used as the destination of a move instruction - Adjusted relationship between Horizontal counter and render events to better match tests/measurements - Adjusted vertical interrupt timing to better match measurements - Improved timing of 68K/Z80 interactions based on tests *Other Changes* - Z80 disassembler now supports a start offset parameter: -s - Windows build now uses link time optimization like the others - Optimized the VDP code - Improved audio resampling - Added Mega Man - The Wily Wars to ROM database - Added Wonder Boy in Monster World to ROM database 0.3.1 ----- *New Features* - BlastEm will now open a new terminal window when starting the debugger if it's not attached to one - Errors are displayed in a message box if no terminal is attached - Pure SDL render path for when OpenGL 2 is unavailable (thanks Higor Eurípedes) *Bugfixes* - GDB remote debugging works again - Fixed a name conflict that prevented vgmplay from being built on OS X *Other Changes* - Windows build now includes the disassemblers, VGM player and save state viewer 0.3.0 ----- *New Features* - 32-bit x86 CPUs are now supported - BlastEm is now available for OS X and Windows - Finished support for the Sega mapper used by Super Street Fighter 2 - Added support for EEPROM saves - Added support for large flat-mapped ROMs (used by some homebrew ROMS) - New 68K debugger command bt (backtrace) - I/O devices (gamepads and other peripherals) can now be configured in blastem.cfg - VDP Debugger views from before 0.1.0 have been restored *Bugfixes* - Partial emulation of floating bus bits for certain I/O regs (fixes Super Offroad 2) - Fixed shadow/highlight mode - Implemented AM and fixed LFO update speed - Fixed negative detune values - Corrected register to operator mapping for channel 3 special mode - Initial stab at emulating cycles being stolen from the 68K when the Z80 accesses its bus - Better handling of creating a savestate in "uncooperative" games/demos - Implemented VSCROLL latching - Fixed a bug that would corrupt the Z80's SP register in some situations - Fixed PAL flag in VDP status register - Fixed HV counter - Fixed flag calculation for RRA, RRCA, RLA and RLCA - Fixed instruction timing for RR, RRC, RL and RLC when using the IX or IY registers - Fixed access to the I and R registers *Other Changes* - Upgraded to SDL2 - Dropped suport for the non-OpenGL render path - Linux builds should now work on something other than the specific version of Ubuntu I happen to be using at build time - BlastEm now syncs at least once per line by default. You can change this behavior with the max_clocks config value 0.2.0 ----- *New Features* - Added Turbo and Slow modes that overclock and underclock the system respectively - Added FPS counter - New OpenGL render backend that allows filters to be written in GLSL - Support 'UI' bindings on gamepad buttons and dpads - GDB remote debugging support - New debugger 68K debugger commands o, s and zp (see README for details) *Bugfixes* - Fixed argument handling so that the ROM filename does not need to be specified first - Don't allow register writes to Mode 5 only regs when in Mode 4 - Fixed a bunch of VDP edge case behavior checked by Nemesis's test ROM - Fixed operator 1 self-feedback - Fixed handling of envelope overflow in attack phase - Fixed handling of channel output overflow - Adjusted FM and PSG volume to better match real hardware - Improved YM-2612 busy flag emulation - Properly sync hardware at end of frame when the 68K is blocked by DMA - Approximate wait state behavior when the Z80 accesses the 68K's bus - Implemented HV counter latch - Implemented sprite overflow and collision flags 0.1.0 ----- Initial Release blastem-0.6.3.4/COPYING000066400000000000000000001045131374760425700143670ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . blastem-0.6.3.4/Makefile000066400000000000000000000214171374760425700147750ustar00rootroot00000000000000#disable built-in rules .SUFFIXES : PKG_CONFIG ?= pkg-config ifndef OS OS:=$(shell uname -s) endif FIXUP:=true BUNDLED_LIBZ:=zlib/adler32.o zlib/compress.o zlib/crc32.o zlib/deflate.o zlib/gzclose.o zlib/gzlib.o zlib/gzread.o\ zlib/gzwrite.o zlib/infback.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o zlib/trees.o zlib/uncompr.o zlib/zutil.o ifeq ($(OS),Windows) GLEW_PREFIX:=glew MEM:=mem_win.o TERMINAL:=terminal_win.o FONT:=nuklear_ui/font_win.o NET:=net_win.o EXE:=.exe SO:=dll CPU:=i686 ifeq ($(CPU),i686) CC:=i686-w64-mingw32-gcc-win32 WINDRES:=i686-w64-mingw32-windres GLUDIR:=Win32 SDL2_PREFIX:="sdl/i686-w64-mingw32" else CC:=x86_64-w64-mingw32-gcc-win32 WINDRES:=x86_64-w64-mingw32-windres SDL2_PREFIX:="sdl/x86_64-w64-mingw32" GLUDIR:=x64 endif GLEW32S_LIB:=$(GLEW_PREFIX)/lib/Release/$(GLUDIR)/glew32s.lib CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration -Wpointer-arith -Werror=pointer-arith LDFLAGS:=-lm -lmingw32 -lws2_32 -mwindows ifneq ($(MAKECMDGOALS),libblastem.dll) CFLAGS+= -I"$(SDL2_PREFIX)/include/SDL2" -I"$(GLEW_PREFIX)/include" -DGLEW_STATIC LDFLAGS+= $(GLEW32S_LIB) -L"$(SDL2_PREFIX)/lib" -lSDL2main -lSDL2 -lopengl32 -lglu32 endif LIBZOBJS=$(BUNDLED_LIBZ) else MEM:=mem.o TERMINAL:=terminal.o NET:=net.o EXE:= HAS_PROC:=$(shell if [ -d /proc ]; then /bin/echo -e -DHAS_PROC; fi) CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration -Wno-unused-value -Wpointer-arith -Werror=pointer-arith $(HAS_PROC) -DHAVE_UNISTD_H -Wno-unused-result ifeq ($(OS),Darwin) LIBS=sdl2 glew FONT:=nuklear_ui/font_mac.o SO:=dylib else SO:=so ifdef USE_FBDEV LIBS=alsa ifndef NOGL LIBS+=glesv2 egl endif CFLAGS+= -DUSE_GLES -DUSE_FBDEV -pthread else ifdef USE_GLES LIBS=sdl2 glesv2 CFLAGS+= -DUSE_GLES else LIBS=sdl2 glew gl endif #USE_GLES endif #USE_FBDEV FONT:=nuklear_ui/font.o endif #Darwin ifdef HOST_ZLIB LIBS+= zlib LIBZOBJS= else LIBZOBJS=$(BUNDLED_LIBZ) endif ifeq ($(OS),Darwin) #This should really be based on whether or not the C compiler is clang rather than based on the OS CFLAGS+= -Wno-logical-op-parentheses endif ifdef PORTABLE ifdef USE_GLES ifndef GLES_LIB GLES_LIB:=$(shell $(PKG_CONFIG) --libs glesv2) endif LDFLAGS:=-lm $(GLES_LIB) else CFLAGS+= -DGLEW_STATIC -Iglew/include LDFLAGS:=-lm glew/lib/libGLEW.a endif ifeq ($(OS),Darwin) SDL_INCLUDE_PATH:=Frameworks/SDL2.framework/Headers CFLAGS+= -mmacosx-version-min=10.10 LDFLAGS+= -FFrameworks -framework SDL2 -framework OpenGL -framework AppKit -mmacosx-version-min=10.10 FIXUP:=install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/Frameworks/SDL2.framework/Versions/A/SDL2 else SDL_INCLUDE_PATH:=sdl/include LDFLAGS+= -Wl,-rpath='$$ORIGIN/lib' -Llib -lSDL2 ifndef USE_GLES LDFLAGS+= $(shell $(PKG_CONFIG) --libs gl) endif endif #Darwin CFLAGS+= -I$(SDL_INCLUDE_PATH) else ifeq ($(MAKECMDGOALS),libblastem.$(SO)) LDFLAGS:=-lm else CFLAGS:=$(shell $(PKG_CONFIG) --cflags-only-I $(LIBS)) $(CFLAGS) LDFLAGS:=-lm $(shell $(PKG_CONFIG) --libs $(LIBS)) ifdef USE_FBDEV LDFLAGS+= -pthread endif endif #libblastem.so ifeq ($(OS),Darwin) LDFLAGS+= -framework OpenGL -framework AppKit endif endif #PORTABLE endif #Windows ifndef OPT ifdef DEBUG OPT:=-g3 -O0 else ifdef NOLTO OPT:=-O2 else OPT:=-O2 -flto -g -fPIE -D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wno-stringop-overflow endif #NOLTO endif #DEBUG endif #OPT CFLAGS:=$(OPT) $(CFLAGS) LDFLAGS:=$(OPT) $(LDFLAGS) -Wl,-z,relro -Wl,-z,now -fPIE -pie ifdef Z80_LOG_ADDRESS CFLAGS+= -DZ80_LOG_ADDRESS endif ifdef PROFILE PROFFLAGS:= -Wl,--no-as-needed -lprofiler -Wl,--as-needed CFLAGS+= -g3 endif ifdef NOGL CFLAGS+= -DDISABLE_OPENGL endif ifdef M68030 CFLAGS+= -DM68030 endif ifdef M68020 CFLAGS+= -DM68020 endif ifdef M68010 CFLAGS+= -DM68010 endif ifndef CPU CPU:=$(shell uname -m) endif #OpenBSD uses different names for these architectures ifeq ($(CPU),amd64) CPU:=x86_64 else ifeq ($(CPU),i386) CPU:=i686 endif endif TRANSOBJS=gen.o backend.o $(MEM) arena.o tern.o M68KOBJS=68kinst.o ifdef NEW_CORE Z80OBJS=z80.o z80inst.o M68KOBJS+= m68k.o CFLAGS+= -DNEW_CORE else Z80OBJS=z80inst.o z80_to_x86.o ifeq ($(CPU),x86_64) M68KOBJS+= m68k_core.o m68k_core_x86.o TRANSOBJS+= gen_x86.o backend_x86.o else ifeq ($(CPU),i686) M68KOBJS+= m68k_core.o m68k_core_x86.o TRANSOBJS+= gen_x86.o backend_x86.o endif endif endif AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o CONFIGOBJS=config.o tern.o util.o paths.o NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o RENDEROBJS=ppm.o controller_info.o ifdef USE_FBDEV RENDEROBJS+= render_fbdev.o else RENDEROBJS+= render_sdl.o endif ifdef NOZLIB CFLAGS+= -DDISABLE_ZLIB else RENDEROBJS+= $(LIBZOBJS) png.o endif MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o $(RENDEROBJS) io.o romdb.o hash.o menu.o xband.o \ realtec.o i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o LIBOBJS=libblastem.o system.o genesis.o debug.o gdb_remote.o vdp.o io.o romdb.o hash.o xband.o realtec.o \ i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o $(LIBZOBJS) ifdef NONUKLEAR CFLAGS+= -DDISABLE_NUKLEAR else MAINOBJS+= $(NUKLEAROBJS) endif ifeq ($(CPU),x86_64) CFLAGS+=-DX86_64 -m64 LDFLAGS+=-m64 else ifeq ($(CPU),i686) CFLAGS+=-DX86_32 -m32 LDFLAGS+=-m32 # else # $(error $(CPU) is not a supported architecture) endif endif ifdef NOZ80 CFLAGS+=-DNO_Z80 else MAINOBJS+= sms.o $(Z80OBJS) LIBOBJS+= sms.o $(Z80OBJS) endif ifeq ($(OS),Windows) MAINOBJS+= res.o endif ifdef CONFIG_PATH CFLAGS+= -DCONFIG_PATH='"'$(CONFIG_PATH)'"' endif ifdef DATA_PATH CFLAGS+= -DDATA_PATH='"'$(DATA_PATH)'"' endif ifdef FONT_PATH CFLAGS+= -DFONT_PATH='"'$(FONT_PATH)'"' endif ALL=dis$(EXE) zdis$(EXE) vgmplay$(EXE) blastem$(EXE) ifneq ($(OS),Windows) ALL+= termhelper endif ifeq ($(MAKECMDGOALS),libblastem.$(SO)) CFLAGS+= -fno-common -fpic -DIS_LIB endif all : $(ALL) libblastem.$(SO) : $(LIBOBJS) $(CC) -shared -o $@ $^ $(LDFLAGS) blastem$(EXE) : $(MAINOBJS) $(CC) -o $@ $^ $(LDFLAGS) $(PROFFLAGS) $(FIXUP) ./$@ blastjag$(EXE) : jaguar.o jag_video.o $(RENDEROBJS) serialize.o $(M68KOBJS) $(TRANSOBJS) $(CONFIGOBJS) $(CC) -o $@ $^ $(LDFLAGS) termhelper : termhelper.o $(CC) -o $@ $^ $(LDFLAGS) dis$(EXE) : dis.o 68kinst.o tern.o vos_program_module.o $(CC) -o $@ $^ $(OPT) $(LDFLAGS) jagdis : jagdis.o jagcpu.o tern.o $(CC) -o $@ $^ $(LDFLAGS) zdis$(EXE) : zdis.o z80inst.o $(CC) -o $@ $^ $(LDFLAGS) libemu68k.a : $(M68KOBJS) $(TRANSOBJS) ar rcs libemu68k.a $(M68KOBJS) $(TRANSOBJS) trans : trans.o serialize.o $(M68KOBJS) $(TRANSOBJS) util.o $(CC) -o $@ $^ $(OPT) transz80 : transz80.o $(Z80OBJS) $(TRANSOBJS) $(CC) -o transz80 transz80.o $(Z80OBJS) $(TRANSOBJS) ztestrun : ztestrun.o serialize.o $(Z80OBJS) $(TRANSOBJS) $(CC) -o ztestrun $^ $(OPT) ztestgen : ztestgen.o z80inst.o $(CC) -ggdb -o ztestgen ztestgen.o z80inst.o vgmplay$(EXE) : vgmplay.o $(RENDEROBJS) serialize.o $(CONFIGOBJS) $(AUDIOOBJS) $(CC) -o $@ $^ $(LDFLAGS) $(FIXUP) ./$@ blastcpm : blastcpm.o util.o serialize.o $(Z80OBJS) $(TRANSOBJS) $(CC) -o $@ $^ $(OPT) $(PROFFLAGS) test : test.o vdp.o $(CC) -o test test.o vdp.o testgst : testgst.o gst.o $(CC) -o testgst testgst.o gst.o test_x86 : test_x86.o gen_x86.o gen.o $(CC) -o test_x86 test_x86.o gen_x86.o gen.o test_arm : test_arm.o gen_arm.o mem.o gen.o $(CC) -o test_arm test_arm.o gen_arm.o mem.o gen.o test_int_timing : test_int_timing.o vdp.o $(CC) -o $@ $^ gen_fib : gen_fib.o gen_x86.o mem.o $(CC) -o gen_fib gen_fib.o gen_x86.o mem.o offsets : offsets.c z80_to_x86.h m68k_core.h $(CC) -o offsets offsets.c vos_prog_info : vos_prog_info.o vos_program_module.o $(CC) -o vos_prog_info vos_prog_info.o vos_program_module.o m68k.c : m68k.cpu cpu_dsl.py ./cpu_dsl.py -d call $< > $@ %.c : %.cpu cpu_dsl.py ./cpu_dsl.py -d goto $< > $@ %.db.c : %.db sed $< -e 's/"/\\"/g' -e 's/^\(.*\)$$/"\1\\n"/' -e'1s/^\(.*\)$$/const char $(shell echo $< | tr '.' '_')_data[] = \1/' -e '$$s/^\(.*\)$$/\1;/' > $@ %.o : %.S $(CC) -c -o $@ $< %.o : %.c $(CC) $(CFLAGS) -c -o $@ $< %.o : %.m $(CC) $(CFLAGS) -c -o $@ $< %.png : %.xcf xcf2png $< > $@ %.tiles : %.spec ./img2tiles.py -s $< $@ %.bin : %.s68 vasmm68k_mot -Fbin -m68000 -no-opt -spaces -o $@ -L $@.list $< %.bin : %.sz8 vasmz80_mot -Fbin -spaces -o $@ $< res.o : blastem.rc $(WINDRES) blastem.rc res.o arrow.tiles : arrow.png cursor.tiles : cursor.png font_interlace_variable.tiles : font_interlace_variable.png button.tiles : button.png font.tiles : font.png menu.bin : font_interlace_variable.tiles arrow.tiles cursor.tiles button.tiles font.tiles clean : rm -rf $(ALL) trans ztestrun ztestgen *.o nuklear_ui/*.o zlib/*.o test blastem-0.6.3.4/README000066400000000000000000000606661374760425700142260ustar00rootroot00000000000000BlastEm 0.6.0 ------------- Installation ------------ Extract this archive to a directory of your choosing. NOTE: Prior to version 0.4.1, BlastEm was still using Unixy locations for config and save files. If you're upgrading from a previous version on Windows, you will need to move them manually. For config files, the relevant paths are in the previous paragraph. For save files, move all the directories found in %userprofile%\.local\share\blastem to %localappdata%\blastem Usage ----- This version of BlastEm has a GUI that allows access to most configuration options. Simply start BlastEm without passing a ROM filename on the command line to access the main menu. You can also access the menu by hitting the button mapped to the ui.exit action (default Esc). If Open GL is disabled or unavaible, or you explicitly request it, the old ROM-based UI will be used instead. This UI does not support configuration so you will need to modify the configuration file manually if you use it. See the rest of this README for instructions on modifying the configuration file. Some operations are currently only supported through the command line. To get a list of supported command line options on Linux or OSX type: ./blastem -h From within your BlastEm directory. On Windows type: blastem.exe -h Lock-On Support --------------- This version of BlastEm has some preliminary support for Sonic & Knuckles lock on technology. This is available via both the menu and the command line. To use it from the menu, first load Sonic & Knuckles normally. Enter the menu (mapped to the Escape key by default) and select the "Lock On" option to select a ROM to lock on. The system will then reload with the combined game. To use it from the command line, specify the Sonic & Knuckles ROM as the primary ROM and specify the ROM to be locked on using the -o option. As an example: ./blastem ~/romz/sonic_and_knuckles.bin -o ~/romz/sonic3.bin Please note that Sonic 2 lock-on does not work at this time. Configuration ------------- Configuration is read from the file at $HOME/.config/blastem/blastem.cfg on Unix-like systems and %localappdata%\blastem\blastem.cfg if it exists. Othwerise it is read from default.cfg from the same directory as the BlastEm executable. Sections are denoted by a section name followed by an open curly bracket, the section's contents and a closing curly bracket. Individual configuration values are set by entering the value's name followed by a space or tab and followed by the desired value. Bindings -------- The keys subsection of bindings maps keyboard keys to gamepad buttons or UI actions. The key name goes on the left and the action is on the right. Most keys are named for the character they produce when pressed. For keys that don't correspond to a normal character, check the list below: Name | Description ----------------- up Up arrow down Down arrow left Left arrow right Right arrow space tab backspace Backspace on PC keyboards, Delete on Mac keyboards esc delete lshift Left shift rshift Right shift lctrl Left control rctrl Right control lalt Left alt on PC keyboards, Option on Mac keyboards ralt Right alt on PC keyboards, Option on Mac keyboards home end pageup pagedown f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 select play search back The pads subsection is used to map gamepads and joysticks. Gamepads that are recognized, can have their buttons and axes mapped with semantic names. Xbox 360, PS4 and PS3 style names are supported. Unrecognized gamepads can be mapped using numeric button and axis ids. The following button names are recognized by BlastEm: a, cross b, circle x, square y, trinagle start, options back, select, share guide leftbutton, l1 rightbutton, r1 leftstick, l3 rightstick, r3 The following axis names are recognized by BlastEm: leftx lefty rightx righty lefttrigger, l2 righttrigger, r2 The mice subsection is used to map mice to emulated Mega/Sega mice. The default configuration maps both the first and second host mice to the first emulated mouse. This should not need modification for most users. One special mapping deserves a mention. By default, the 'r' key is mapped to ui.release_mouse. When operating in windowed mode the mouse has a capture behavior. Mouse events are ignored until you click in the window. The mouse will then be "captured" and the cursor will be both made invisible and locked to the window. The ui.release_mouse binding releases the mouse so it can be used normally. UI Actions ---------- This section lists the various "UI" actions that can be triggered by a key or gamepad binding. ui.release_mouse Releases the mouse if it is currently captured ui.plane_debug Toggles the VDP plane debug view ui.vram_debug Toggles the VDP VRAM debug view ui.cram_debug Toggles the VDP CRAM debug view ui.compositing_debug Toggles the VDP compositing debug view ui.vdp_debug_mode Cycles the mode/palette of the VDP debug view that currently has focus ui.enter_debugger Enters the debugger for the main CPU of the currently emulated system ui.screenshot Takes an internal screenshot ui.exit Returns to the menu ROM if currently in a game that was launched from the menu. Exits otherwise ui.save_state Saves a savestate to the quicksave slot ui.set_speed.N Selects a specific machine speed specified by N which should be a number between 0-9. Speeds are specified in the "clocks" section of the config ui.next_speed Selects the next machine speed ui.prev_speed Selects the previous machine speed ui.toggle_fullscreen Toggles between fullscreen and windowed mode ui.soft_reset Resets a portion of the emulated machine Equivalent to pushing the reset button on the emulated device ui.reload Reloads the current ROM from a file and performs a hard reset of the emulated device ui.sms_pause Triggers a press of the pause button when in SMS mode ui.toggle_keyboard_captured Toggles the capture state of the host keyboard when an emulated keyboard is present IO -- This section controls which peripherals are attached to the emulated console. IO assignments can be overridden by the ROM database when appropriate. For instance, games with mouse support can automatically use the mouse and games that only support 3-button pads can automatically force an appropriate pad. Unforunately, the ROM database is not yet exhaustive so manual configuration may be needed here in some cases. Video ----- The video section contains settings that affect the visual output of BlastEm. "aspect" is used to control the aspect ratio of the emulated display. The default of 4:3 matches that of a standard definition television. "width" is used to control the window width when not in fullscreen mode. "height" is used to control the window height when not in fullscreen mode. If left unspecified, it will be calculated from "width" and "aspect". "vertex_shader" and "fragment_shader" define the GLSL shader program that produces the final image for each frame. Shaders can be used to add various visual effects or enhancements. Currently BlastEm only ships with the default shader and a "subtle" crt shader. If you write your own shaders, place them in $HOME/.config/blastem/shaders and then specify the basename of the new shader files in the "vertex_shader" and "fragment_shader" config options. Note that shaders are not available in the SDL fallback renderer. "scanlines" controls whether there is any emulation of the gaps between display lines that are present when driving a CRT television with a 240p signal. This emulation is very basic at the moment so this option is off by default. "vsync" controls whether the drawing of frames is synchronized to the monitor refresh rate. Valid values for this setting are "off", "on" and "tear". The latter will attempt to use the "late tear" option if it's available and normal vsync otherwise. Currently it's recommended to leave this at the default of "off" as it may not work well with the default "audio" sync method and the "video" sync method will automatically enable "vsync". See "Sync Source and VSync" for more details. "fullscreen" controls whether BlastEm starts in fullscreen or windowed mode. This can be overridden on the command line with the -f flag. If fullscreen is set to "off", -f will turn it on. Conversely, if fullscreen is set to "on" in the config, -f will turn it off. "gl" controls whether OpenGL is used for rendering. The default value is on. If it is set to off instead, the fallback renderer which uses SDL2's render API will be used instead. This option is mostly useful for users on hardware that lacks OpenGL 2 support. While BlastEm will fall back automatically even if gl is set to on there will be a warning. Disabling gl eliminates this warning. "scaling" controls the type of scaling used for textures in both the GL and SDL renderers. Valid values are "nearest" and "linear". Note that shaders also impact how pixels are scaled. The "ntsc" and "pal" sub-sections control overscan settings for the emulated video output for NTSC and PAL consoles respectively. More details are available in the Overscan section. Overscan -------- Analog televisions generally don't display the entirety of a video frame. Some portion is cropped at the edges of the display. This is called overscan. Unfortunately, the amount of cropping performed varies considerably and is even adjustable on many TV sets. To deal with this, BlastEm allows overscan to be customized. Overscan values are specified in the "ntsc" and "pal" sub-sections of the "video" section of the config file. The "overscan" sub-section contains four settings for specifying the number of pixels cropped on each side of the display: "top", "bottom", "left" and "right". The default settings hide the horizontal border completely for both NTSC and PAL consoles. For the vertical borders, the NTSC overscan settings are chosen to give square pixels with the default aspect ratio of 4:3. For PAL, the default settings are set so that the PAL-exclusive V30 mode will produce a visible border that is the same size as what is shown in V28 mode in NTSC. This results in a slightly squished picture compared to NTSC which is probably appropriate given that a PAL display has more lines than an NTSC one. Audio ----- The audio section contains settings that affect the audio output of BlastEm. "rate" selects the preferred sample rate for audio output. Your operating system may not accept this value in which case a different rate will be chosen. This should generally be either the native sample rate of your sound card or an integral divisor of it. Most modern sound cards have a native output rate that is a multiple of 48000 Hz so the default setting should work well for most users. "buffer size" controls how large of a buffer uses for audio data. Smaller values will reduce latency, but too small of a value can lead to dropouts. 512 works well for me, but a higher or lower value may be more appropriate for your system. "lowpass_cutoff" controls the cutoff, or knee, frequency of the RC-style low-pass filter. The default value of 3390 Hz is supposedly what is present in at least some Genesis/Megadrive models. Other models reportedly use an even lower value. "gain" specifies the gain in decibels to be applied to the overall output. "fm_gain" specifies the gain to be applied to the emulated FM output before mixing with the PSG. "psg_gain" specifies the gain to be applied to the emulated PSG output before mixing with the FM chip. "fm_dac" controls the characteristics of the DAC in the emulated FM chip. If this is set to "linear", then the DAC will have precise linear output similar to the integrated YM3438 in later Gen/MD consoles. If it is set to "zero_offset", there will be a larger gap between -1 and 0. This is commonly referred to as the "ladder effect". This will also cause "leakage" on channels that are muted or panned to one side in a similar manner to a discrete YM2612. Clocks ------ The clocks section contains settings that affect how fast things run. "m68k_divider" describes the relationsip between the master clock (which is 53693175 Hz for NTSC mode and 53203395 Hz for PAL mode). The default value of 7 matches the real hardware. Set this to a lower number to overclock the 68000 and set it to a higher number to underclock it. "max_cycles" controls how often the system is forced to synchronize all hardware. BlastEm generally uses a sync on demand approach to synchronizing components in the system. This can provide perfect synchronization for most components, but since the Z80 can steal cycles from the 68000 at unpredictable times 68000/Z80 synchronization is imperfect. The default value of 3420 corresponds to the number of master clock cycles per line. Larger numbers may produce a modest performance improvement whereas smaller numbers will improve 68000/Z80 synchronization. "speeds" controls the speed of the overall emulated console at different presets. Preset 0 is the default speed and should normally be set to 100. The other presets enable the slow/turbo mode functionality. UI -- The UI section contains settings that affect the user interface. "rom" determines the path of the Genesis/Megadrive ROM that implements the UI. Relative paths will be loaded relative to the BlastEm executable. "initial_path" specifies the starting path for the ROM browser. It can contain the following special variables: $HOME, $EXEDIR. Additionally, variables defined in the OS environment can be used. "remember_path" specifies whether BlastEm should remember the last path used in the file browser. When it is set to "on", the last path will be remembered and used instead of "initial_path" in subsequent runs. If it is set to "off", "initial_path" will always be used. "screenshot_path" specifies the directory "internal" screenshots will be saved in. It accepts the same special variables as "initial_path". "screenshot_template" specifies a template for creating screenshot filenames. It is specified as a format string for the C library function strftime "save_path" specifies the directory that savestates, SRAM and EEPROM data will be saved in for a given game. It can contain the following special variables: $HOME, $EXEDIR, $USERDATA, $ROMNAME. Like "initial_path" it can also reference variables from the environment. "extensions" specifies the file extensions that should be displayed in the file browser. "state_format" specifies the preferred format for saving save states. Valid values are "native" (the default) and "gst". "native" save states do a better job of preserving the state of the emulated system, but "gst" save states are compatible with other emulators like Kega and Gens. This setting has no effect for systems other than the Genesis/Mega Drive Path Variables -------------- This section explains the meaning of the special path variables referenced in the previous section. $HOME The home directory of the current user. On most Unix variants, it will be a subdirectory of /home. On Windows it will typically be a subdirectory of C:\Users $EXEDIR The directory the BlastEm executable is located in $USERDATA This is an OS-specific path used for storing application specific user data. On Unix variants, it will be $HOME/.local/share/blastem On Windows it will be %LOCALDATA%/blastem $ROMNAME The name of the currently loaded ROM file without the extension System ------ "ram_init" determines how the RAM in the emulated system is initialized. The default value of "zero" will cause all RAM to be zeroed out before the system is started. Alternatively, "random" can be used to initialize RAM with values from a pseudo-random number generator. This option is mostly useful for developers that want to debug initialization issues in their code. "default_region" determines the console region that will be used when region detection fails and when there are multiple valid regions. The default of 'U' specifies a 60Hz "foreign" console. "sync_source" controls whether BlastEm uses audio or video output to control execution speed. "video" can provide a smoother experience when your display has a similar refresh rate to the emulated system, but has some limitations in the current version. The default value is "audio". "megawifi" enables or disables support for MegaWiFi cart emulation. MegaWiFi is a cartridge that contains WiFi hardware for network functionality. Enabling this means that ROMs potentially have access to your network (and the internet) which obviously has security implications. For this reason, it is disabled by default. If you wish to try out MegaWiFi emulation, set this to "on". Note that the support for MegaWiFi hardware is preliminary in this release. Debugger -------- BlastEm has an integrated command-line debugger loosely based on GDB's interface. The interface is very rough at the moment. Available commands in the 68K debugger are: b ADDRESS - Set a breakpoint at ADDRESS d BREAKPOINT - Delete a 68K breakpoint co BREAKPOINT - Run a list of debugger commands each time BREAKPOINT is hit a ADDRESS - Advance to address n - Advance to next instruction o - Advance to next instruction ignoring branches to lower addresses (good for breaking out of loops) s - Advance to next instruction (follows bsr/jsr) c - Continue bt - Print a backtrace p[/(x|X|d|c)] VALUE - Print a register or memory location di[/(x|X|d|c)] VALUE - Print a register or memory location each time a breakpoint is hit vs - Print VDP sprite list vr - Print VDP register info zb ADDRESS - Set a Z80 breakpoint zp[/(x|X|d|c)] VALUE - Display a Z80 value q - Quit BlastEm Available commands in the Z80 debugger are: b ADDRESS - Set a breakpoint at ADDRESS de BREAKPOINT - Delete a Z80 breakpoint a ADDRESS - Advance to address n - Advance to next instruction c - Continue p[/(x|X|d|c)] VALUE - Print a register or memory location di[/(x|X|d|c)] VALUE - Print a register or memory location each time a breakpoint is hit q - Quit BlastEm The -d flag can be used to cause BlastEm to start in the debugger. Alternatively, you can use the ui.enter_debugger action (mapped to the 'u' key by default) to enter the debugger while a game is running. To debug the menu ROM, use the -dm flag. GDB Remote Debugging -------------------- In addition to the native debugger, BlastEm can also act as a GDB remote debugging stub. To use this, you'll want to configure your Makefile to produce both an ELF executable and a raw binary. Invoke an m68k-elf targeted gdb with the ELF file. Once inside the gdb session, type: target remote | BLASTEM_PATH/blastem ROM_FILE.bin -D where BLASTEM_PATH is the relative or absolute path to your BlastEm installation and ROM_FILE.bin is the name of the raw binary for your program. BlastEm will halt at the beginning of your program's entry point and return control to GDB. This will allow you to set breakpoints before your code runs. On Windows, the procedure is slightly different. First run blastem.exe ROM_FILE.bin -D This will cause BlastEm to wait for a socket connection on port 1234. It will appear to be frozen until gdb connects to it. Now open the ELF file in gdb and type: target remote :1234 Trace points and watch points are not currently supported. Included Tools -------------- BlastEm ships with a few small utilities that leverage portions of the emulator code. dis - 68K disassembler zdis - Z80 disassembler vgmplay - Very basic VGM player stateview - GST save state viewer Sync Source and VSync ----- This section includes information about using VSync with BlastEm. Currently, the best way to use VSync is to set the sync source to "video". This will force VSync on and use video output for controlling the speed of emulation. In this mode, audio will have it's rate automatically adjusted to keep pace with video. The code for this is still a bit immature, so you may experience dropouts or pitch changes in this mode. If you experience problems, please switch back to the "audio" sync source, which is the default. You can also enable vsync when using the "audio" sync source by changing the "vsync" setting. This will generally work okay as long as the emulated refresh rate is below your monitor refresh rate (even if only slightly), but you will occassionally get a doubled frame (or frequently if the refresh rates are very different). Turbo mode will currently not work when vsync is on, regardless of which sync source is used. Slow mode will work with "audio" sync, but not "video" sync. -------------- My work has been made much easier by the contributions of those in the Genesis community past and present. I'd like to thank the people below for their help. Nemesis - His work reverse engineering and documenting the VDP and YM-2612 has saved me an immeasurable amount of time. I've found both his sprite overflow test ROM and VDP FIFO Testing ROM to be quite helpful. Charles MacDonald - While it hasn't been updated in a while, I still find his VDP document to be my favorite reference. His Genesis hardware document has also come in handy. Eke-Eke - Eke-Eke wrote a great document on the use of I2C EEPROM in Genesis games and also left some useful very helpful comments about problematic games in Genesis Plus GX Sauraen - Sauraen has analyzed the YM2203 and YM2612 dies and written a VHDL operator implementation. These have been useful in improving the accuracy of my YM2612 core. Alexey Khokholov - Alexey (aka Nuke.YKT) has analyzed the YM3438 die and written a fairly direct C implementation from that analysis. This has been a useful reference for verifying and improving my YM2612 core. Bart Trzynadlowski - His documents on the Genecyst save-state format and the mapper used in Super Street Fighter 2 were definitely appreciated. KanedaFR - Kaneda's SpritesMind forum is a great resource for the Sega development community. Titan - Titan has created what are without a doubt the most impressive demos on the Megadrive. Additionally, I am very grateful for the documentation provided by Kabuto and the assistance of Kabuto, Sik and Jorge in getting Overdrive 2 to run properly in BlastEm. flamewing - flamewing created a very handy exhaustive test ROM for 68K BCD instructions and documented the proper behavior for certain BCD edge cases r57shell - r57shell created a test ROM for 68K instruction sizes that was invaluable in fixing the remaining bugs in my 68K instruction decoder I'd also like to thank the following people who have performed compatibility testing or submitted helpful bug reports micky, Sasha, lol-frank, Sik, Tim Lawrence, ComradeOj, Vladikcomper License ------- BlastEm is free software distributed under the terms of the GNU General Public License version 3 or higher. This gives you the right to redistribute and/or modify the program as long as you follow the terms of the license. See the file COPYING for full license details. Binary releases of BlastEm are packaged with GLEW, SDL2 and zlib which have their own licenses. See GLEW-LICENSE and SDL-LICENSE for details. For zlib license information, please see zlib.h in the source code release. blastem-0.6.3.4/README.md000066400000000000000000000025161374760425700146130ustar00rootroot00000000000000BlastEm: The fast and accurate Genesis emulator =============================================== [BlastEm](https://www.retrodev.com/blastem) is a highly precise multi-system emulator that emulates the Sega Genesis/Mega Drive, Master System and Game Gear developed by Michael Pavone. Despite this high accuracy, even the most demanding software runs at full speed on modest hardware. Requirements: ------------- [BlastEm](https://www.retrodev.com/blastem) depends on SDL2, GLEW and OpenGL, all architectures that are supported must be able to run the emulator. **Debian/Ubuntu/GNU/Linux** # apt install gcc libasound2-dev libglew-dev libsdl2-dev libsdl2-mixer-dev zlib1g-dev make Installation: ------------- Next you need to compile this release. **Compilation** $ make **Once completed, run the created binary** $ ./blastem **Removing compilation** $ make clean License: -------- > **BlastEm** is free software distributed under the terms of the > GNU General Public License version 3 or higher. > This gives you the right to redistribute and/or modify the program as long > as you follow the terms of the license. See the file COPYING for full license > details. > Binary releases of **BlastEm** are packaged with **GLEW** and **SDL2** which > have their own licenses. - Copyright (c) 2012-2020 BlastEm by **Michael Pavone** blastem-0.6.3.4/analyze.py000066400000000000000000000114671374760425700153560ustar00rootroot00000000000000#!/usr/bin/env python #OLD #0 - !SE #1 - !CAS #2 - A0 #3 - A1 #------ #4 - A2 #5 - A3 #6 - A7 #7 - EDCLK #------ #8 - !HSYNC #9 - A4 #A - A5 #B - A6 #------ #C - !RAS #D - !WB/!WE #E - !DT/!OE #F - SC #NEW #0 - !IPL2 #1 - !CAS #2 - A0 #3 - A1 #------ #4 - A2 #5 - A3 #6 - A7 #7 - !HSYNC #------ #8 - !VSYNC #9 - A4 #A - A5 #B - A6 #------ #C - !RAS #D - !WB/!WE #E - !DT/!OE #F - SC #VRAM swizzling #A0 = V0 #A1 = V1 #A8 = V2 #A9 = V3 #A10 = V4 #A11 = V5 #A12 = V6 #A13 = V7 #A14 = V8 #A15 = V9 #--guesses follow-- #A2 = V10 #A3 = V11 #A4 = V12 #A5 = V13 #A6 = V14 #A7 = V15 def get_addr(sample): return ((sample >> 2) & 0xF) | ((sample >> 5) & 0x70) | ((sample << 1) & 0x80) def swizzle_addr(addr): return (addr & 0x0003) | ((addr >> 6) & 0x03FC) | ((addr << 8) & 0xFC00) def print_addr_op(addr, addr_format, mode, samplenum, triggerpos, rate): print '{0:{1}} ({2:{1}}) {3}@{4} ns'.format(swizzle_addr(addr), addr_format, addr, mode, (samplenum - triggerpos)*rate) def detect_rise(last, sample, bit): mask = 1 << bit return (not last & mask) and (sample & mask) def detect_fall(last, sample, bit): mask = 1 << bit return (last & mask) and (not sample & mask) def detect_high(sample, bit): mask = 1 << bit return sample & mask ipl2 = 0x0 cas = 0x1 ras = 0xC vsync = 0x8 hsync = 0x7 wewb = 0xD oedt = 0xE sc = 0xF last = False state = 'begin' triggerpos = 0 readcounter = 0 sillyread = 0 lastaddr = -1 edclk_ticks = 0 sc_ticks = 0 tick_start = False #f = open('street_fighter_vram_100mhz_hsync_trig_2.ols') #f = open('street_fighter_vram_50mhz_hsync_trig.ols') from sys import argv,exit if len(argv) < 2: print 'usage: analyze.py filename' exit(1) if '-b' in argv: addr_format = '016b' else: addr_format = '04X' f = open(argv[1]) for line in f: if line.startswith(';TriggerPosition'): _,_,triggerpos = line.partition(':') triggerpos = int(triggerpos.strip()) elif line.startswith(';Rate'): _,_,rate = line.partition(':') #convert to nanoseconds between samples rate = (1.0/float(rate.strip())) * 1000000000.0 elif not line.startswith(';'): sample,_,samplenum = line.partition('@') samplenum = int(samplenum.strip()) sample = int(sample, 16) if detect_rise(last, sample, sc): sc_ticks += 1 if not (last is False): #detect falling edge of !HSYNC if detect_fall(last, sample, hsync): if readcounter: print readcounter, 'reads,', sillyread, 'redundant reads' readcounter = sillyread = 0 if not tick_start is False: print 'SC:', sc_ticks, ' ticks, {0}MHz'.format(float(sc_ticks)/((rate * (samplenum-tick_start)) / 1000.0)) tick_start = samplenum edclk_ticks = sc_ticks = 0 print 'HSYNC Start @ {0} ns'.format((samplenum - triggerpos)*rate) #detect rising edge of !HSYNC elif detect_rise(last, sample, hsync): if not tick_start is False: float(edclk_ticks)/((rate * (samplenum-tick_start)) / 1000.0) print 'EDCLK:', edclk_ticks, ' ticks, {0}MHz'.format(float(edclk_ticks)/((rate * (samplenum-tick_start)) / 1000.0)) print 'SC:', sc_ticks, ' ticks, {0}MHz'.format(float(sc_ticks)/((rate * (samplenum-tick_start)) / 1000.0)) tick_start = samplenum edclk_ticks = sc_ticks = 0 print 'HSYNC End @ {0} ns'.format((samplenum - triggerpos)*rate) if detect_fall(last, sample, vsync): print 'VSYNC Start @ {0} ns'.format((samplenum - triggerpos)*rate) elif detect_rise(last, sample, vsync): print 'VSYNC End @ {0} ns'.format((samplenum - triggerpos)*rate) if detect_fall(last, sample, ipl2): print 'IPL2 Low @ {0} ns'.format((samplenum - triggerpos)*rate) elif detect_rise(last, sample, ipl2): print 'IPL2 High @ {0} ns'.format((samplenum - triggerpos)*rate) if state == 'begin': #detect falling edge of !RAS if detect_fall(last, sample, ras): state = 'ras' row = get_addr(sample) mode = 'ram' if detect_high(sample, oedt) else 'read transfer' elif detect_fall(last, sample, cas) and detect_high(sample, oedt): state = 'cas' elif state == 'ras': if detect_fall(last, sample, cas): state = 'begin' col = get_addr(sample) addr = (row << 8) | col if mode == 'ram': state = 'ras_cas' else: print_addr_op(addr, addr_format, mode, samplenum, triggerpos, rate) lastaddr = addr #print '{0:04X} {1} - {2:02X}:{3:02X} - {0:016b}'.format(addr, mode, row, col) elif state == 'cas': if detect_fall(last, sample, ras): state = 'begin' print 'refresh@{0} ns'.format((samplenum - triggerpos)*rate) elif state == 'ras_cas': if detect_fall(last, sample, oedt): readcounter += 1 if addr == lastaddr: sillyread += 1 print_addr_op(addr, addr_format, 'read', samplenum, triggerpos, rate) state = 'begin' elif detect_fall(last, sample, wewb): print_addr_op(addr, addr_format, 'write', samplenum, triggerpos, rate) state = 'begin' last = sample blastem-0.6.3.4/analyze_olp.py000066400000000000000000000135161374760425700162250ustar00rootroot00000000000000#!/usr/bin/env python from zipfile import ZipFile from sys import exit, argv def detect_rise(last, sample, bit): mask = 1 << bit return (not last & mask) and (sample & mask) def detect_fall(last, sample, bit): mask = 1 << bit return (last & mask) and (not sample & mask) def detect_high(sample, bit): mask = 1 << bit return sample & mask def detect_low(sample, bit): mask = 1 << bit return not sample & mask def get_value(sample, bits): value = 0 for i in xrange(0, len(bits)): bit = bits[i] value |= (sample >> bit & 1) << i return value def swizzle_mode4(row, col): return (col & 1) | (row << 1) | (col << 8 & 0xFE00) def analyze_delays(chanmap, datafile): if 'M68K_CLK' in chanmap: m68k_clk = chanmap['M68K CLK'] elif 'CLK' in chanmap: m68k_clk = chanmap['CLK'] m_as = chanmap['!AS'] ram_oe = chanmap['RAM !LOE/!RFSH'] ram_ce = chanmap['RAM !CE'] last = False prev = False prevRefresh = False clks = 0 as_start = 0 for line in datafile.readlines(): line = line.strip() if line and not line.startswith(';'): sample,_,num = line.partition('@') sample = int(sample, 16) if not (last is False): if detect_rise(last, sample, m68k_clk): clks = clks + 1 if detect_rise(last, sample, m_as): as_clks = clks - as_start if as_clks > 2: if not (prev is False): print '!AS held for', as_clks, 'cycles starting (delay of ' + str(as_clks - 2) + ') at', as_start, 'and ending at', clks, 'delta since last delay:', as_start - prev else: print '!AS held for', as_clks, 'cycles starting (delay of ' + str(as_clks - 2) + ') at', as_start, 'and ending at', clks prev = as_start elif detect_fall(last, sample, m_as): as_start = clks if detect_fall(last, sample, ram_oe) and detect_high( sample, ram_ce): if prevRefresh is False: print 'RAM refresh at ', clks else: print 'RAM refresh at', clks, 'delta since last:', clks-prevRefresh prevRefresh = clks last = sample def analyze_refresh(chanmap, datafile): if 'M68K_CLK' in chanmap: m68k_clk = chanmap['M68K CLK'] elif 'CLK' in chanmap: m68k_clk = chanmap['CLK'] ram_oe = chanmap['RAM !LOE/!RFSH'] ram_ce = chanmap['RAM !CE'] clks = 0 last = False prevRefresh = False for line in datafile.readlines(): line = line.strip() if line and not line.startswith(';'): sample,_,num = line.partition('@') sample = int(sample, 16) if not (last is False): if detect_rise(last, sample, m68k_clk): clks = clks + 1 if detect_fall(last, sample, ram_oe) and detect_high( sample, ram_ce): if prevRefresh is False: print 'RAM refresh at ', clks else: print 'RAM refresh at', clks, 'delta since last:', clks-prevRefresh prevRefresh = clks last = sample table_start = 0x3800 table_end = table_start + 0x600 sat_start = 0x3E00 #0x3F00 sat_xname = sat_start + 0x80 sat_end = sat_start + 0x100 def analyze_vram(chanmap, datafile): address_bits = [chanmap['AD{0}'.format(i)] for i in xrange(0, 8)] ras = chanmap['!RAS'] cas = chanmap['!CAS'] hsync = chanmap['!HSYNC'] state = 'begin' last = False for line in datafile.readlines(): line = line.strip() if line and not line.startswith(';'): sample,_,num = line.partition('@') sample = int(sample, 16) if not (last is False): if detect_fall(last, sample, hsync): print 'HSYNC low @ {0}'.format(num) elif detect_rise(last, sample, hsync): print 'HSYNC high @ {0}'.format(num) if state == 'begin': if detect_fall(last, sample, ras): state = 'ras' row = get_value(sample, address_bits) elif detect_fall(last, sample, cas): state = 'cas' elif state == 'ras': if detect_fall(last, sample, cas): col = get_value(sample, address_bits) address = swizzle_mode4(row, col) if address < table_end and address >= table_start: offset = (address - table_start)/2 desc = 'Map Row {0} Col {1}'.format(offset / 32, offset & 31) elif address >= sat_start and address < sat_xname: offset = address - sat_start desc = 'Sprite {0} Y Read'.format(offset) elif address >= sat_xname and address < sat_end: offset = address - sat_xname desc = 'Sprite {0} X/Name Read'.format(offset / 2) else: desc = 'Tile {0} Row {1}'.format(address / 32, ((address / 4) & 7) + (0.5 if address & 2 else 0)) print '{0:02X}:{1:02X} - {2:04X} @ {3} - {4}'.format(row, col, address, num, desc) state = 'begin' elif state == 'cas': if detect_fall(last, sample, ras): print 'refresh @ {0}'.format(num) state = 'begin' last = sample def analyze_z80_mreq(chanmap, datafile): m1 = chanmap['!M1'] mreq = chanmap['!MREQ'] addressMask = 0x3FF last = None lastWasM1 = False for line in datafile.readlines(): line = line.strip() if line and not line.startswith(';'): sample,_,num = line.partition('@') sample = int(sample, 16) if not (last is None): if detect_rise(last, sample, mreq): address = last & addressMask if detect_low(last, m1): print 'M1 read {0:02X} @ {1}'.format(address, num) lastWasM1 = True elif lastWasM1: print 'Refresh {0:02X} @ {1}'.format(address, num) lastWasM1 = False else: print 'Access {0:02X} @ {1}'.format(address, num) last = sample def main(args): if len(args) < 2: print 'Usage: analyze_olp.py filename' exit(1) olpfile = ZipFile(args[1], "r") channelfile = olpfile.open('channel.labels') channels = [line.strip() for line in channelfile.readlines()] channelfile.close() print channels chanmap = {} for i in xrange(0, len(channels)): chanmap[channels[i]] = i datafile = olpfile.open('data.ols') #analyze_delays(chanmap, datafile) #analyze_vram(chanmap, datafile) #analyze_refresh(chanmap, datafile) analyze_z80_mreq(chanmap, datafile) datafile.close() if __name__ == '__main__': main(argv) blastem-0.6.3.4/android/000077500000000000000000000000001374760425700147505ustar00rootroot00000000000000blastem-0.6.3.4/android/AndroidManifest.xml000066400000000000000000000043671374760425700205530ustar00rootroot00000000000000 blastem-0.6.3.4/android/ant.properties000066400000000000000000000012721374760425700176520ustar00rootroot00000000000000# This file is used to override default values used by the Ant build system. # # This file must be checked into Version Control Systems, as it is # integral to the build system of your project. # This file is only used by the Ant script. # You can use this to override default values such as # 'source.dir' for the location of your java source folder and # 'out.dir' for the location of your output folder. # You can also use it define how the release builds are signed by declaring # the following properties: # 'key.store' for the location of your keystore and # 'key.alias' for the name of the key to use. # The password will be asked during the build when you use the 'release' target. blastem-0.6.3.4/android/assets/000077500000000000000000000000001374760425700162525ustar00rootroot00000000000000blastem-0.6.3.4/android/assets/default.cfg000066400000000000000000000206421374760425700203630ustar00rootroot00000000000000 bindings { keys { up gamepads.1.up down gamepads.1.down left gamepads.1.left right gamepads.1.right a gamepads.1.a s gamepads.1.b d gamepads.1.c q gamepads.1.x w gamepads.1.y e gamepads.1.z f gamepads.1.mode enter gamepads.1.start r ui.release_mouse [ ui.vdp_debug_mode u ui.enter_debugger p ui.screenshot b ui.plane_debug v ui.vram_debug c ui.cram_debug n ui.compositing_debug esc ui.exit ` ui.save_state 0 ui.set_speed.0 1 ui.set_speed.1 2 ui.set_speed.2 3 ui.set_speed.3 4 ui.set_speed.4 5 ui.set_speed.5 6 ui.set_speed.6 7 ui.set_speed.7 = ui.next_speed - ui.prev_speed f11 ui.toggle_fullscreen tab ui.soft_reset f5 ui.reload z ui.sms_pause rctrl ui.toggle_keyboard_captured select gamepads.1.c play gamepads.1.start back ui.exit } pads { default { dpads { 0 { up gamepads.n.up down gamepads.n.down left gamepads.n.left right gamepads.n.right } } buttons { a gamepads.n.a b gamepads.n.b rightshoulder gamepads.n.c x gamepads.n.x y gamepads.n.y leftshoulder gamepads.n.z back gamepads.n.mode start gamepads.n.start guide ui.exit leftstick ui.save_state } axes { lefty.positive gamepads.n.down lefty.negative gamepads.n.up leftx.positive gamepads.n.right leftx.negative gamepads.n.left lefttrigger ui.prev_speed righttrigger ui.next_speed } } ps4_6b_right { axes { lefttrigger ui.next_speed leftx.negative gamepads.n.up leftx.positive gamepads.n.down lefty.negative gamepads.n.left lefty.positive gamepads.n.right righttrigger gamepads.n.c } buttons { a gamepads.n.a b gamepads.n.b back ui.sms_pause guide ui.exit leftshoulder gamepads.n.mode leftstick ui.save_state rightshoulder gamepads.n.z rightstick ui.prev_speed start gamepads.n.start x gamepads.n.x y gamepads.n.y } dpads { 0 { down gamepads.n.down left gamepads.n.left right gamepads.n.right up gamepads.n.up } } } ps3_6b_right { axes { lefttrigger ui.next_speed leftx.negative gamepads.n.up leftx.positive gamepads.n.down lefty.negative gamepads.n.left lefty.positive gamepads.n.right righttrigger gamepads.n.c } buttons { a gamepads.n.a b gamepads.n.b back ui.sms_pause guide ui.exit leftshoulder gamepads.n.mode leftstick ui.save_state rightshoulder gamepads.n.z rightstick ui.prev_speed start gamepads.n.start x gamepads.n.x y gamepads.n.y } dpads { 0 { down gamepads.n.down left gamepads.n.left right gamepads.n.right up gamepads.n.up } } } xbox_360_6b_right { axes { lefttrigger ui.next_speed leftx.negative gamepads.n.up leftx.positive gamepads.n.down lefty.negative gamepads.n.left lefty.positive gamepads.n.right righttrigger gamepads.n.c } buttons { a gamepads.n.a b gamepads.n.b back ui.sms_pause guide ui.exit leftshoulder gamepads.n.mode leftstick ui.save_state rightshoulder gamepads.n.z rightstick ui.prev_speed start gamepads.n.start x gamepads.n.x y gamepads.n.y } dpads { 0 { down gamepads.n.down left gamepads.n.left right gamepads.n.right up gamepads.n.up } } } xbone_6b_right { axes { lefttrigger ui.next_speed leftx.negative gamepads.n.up leftx.positive gamepads.n.down lefty.negative gamepads.n.left lefty.positive gamepads.n.right righttrigger gamepads.n.c } buttons { a gamepads.n.a b gamepads.n.b back ui.sms_pause guide ui.exit leftshoulder gamepads.n.mode leftstick ui.save_state rightshoulder gamepads.n.z rightstick ui.prev_speed start gamepads.n.start x gamepads.n.x y gamepads.n.y } dpads { 0 { down gamepads.n.down left gamepads.n.left right gamepads.n.right up gamepads.n.up } } } genesis_6b_bumpers { axes { lefttrigger ui.exit righttrigger gamepads.n.mode } buttons { a gamepads.n.a b gamepads.n.b back ui.sms_pause guide ui.exit leftshoulder gamepads.n.z rightshoulder gamepads.n.c start gamepads.n.start x gamepads.n.x y gamepads.n.y } dpads { 0 { down gamepads.n.down left gamepads.n.left right gamepads.n.right up gamepads.n.up } } } saturn_6b_bumpers { axes { lefttrigger ui.exit righttrigger gamepads.n.mode } buttons { a gamepads.n.a b gamepads.n.b back ui.sms_pause guide ui.exit leftshoulder gamepads.n.z rightshoulder gamepads.n.c start gamepads.n.start x gamepads.n.x y gamepads.n.y } dpads { 0 { down gamepads.n.down left gamepads.n.left right gamepads.n.right up gamepads.n.up } } } } mice { 0 { motion mouse.1.motion buttons { 1 mouse.1.left 2 mouse.1.middle 3 mouse.1.right 4 mouse.1.start } } #having the second host mouse also mapped to the first emulated #mouse is useful for laptop users with an external mouse 1 { motion mouse.1.motion buttons { 1 mouse.1.left 2 mouse.1.middle 3 mouse.1.right 4 mouse.1.start } } } } io { devices { 1 gamepad6.1 2 gamepad6.2 } } video { #special value "stretch" will cause aspect to match window aspect ratio aspect 4:3 width 640 #height is normally calculated automatically from width using the aspect setting #if you would like to set it explicitly, uncomment the line below #height 480 vertex_shader default.v.glsl fragment_shader default.f.glsl scanlines off vsync off fullscreen off #setting gl to off, will force use of the SDL2 fallback renderer #this is useful for those running on machines with Open GL 2.0 unavailable #so the warning doesn't display on startup gl on #scaling can be linear (for linear interpolation) or nearest (for nearest neighbor) scaling linear ntsc { overscan { #these values will result in square pixels in H40 mode top 2 bottom 1 #if you want to completely hide the border instead #comment out those two lines and uncomment these #top 11 #bottom 8 #these values will completely hide the horizontal border left 13 right 14 } } pal { overscan { #these values will produce the same size border in V30 mode #as the default NTSC settings will produce in V24 mode #this results in a slightly vertically squished picture #which is probably approximately correct on a properly calibrated TV top 21 bottom 17 #for square pixels and zero border in V30 mode #coment out those two lines and uncomment these #top 30 #bottom 24 #these values will completely hide the horizontal border left 13 right 14 } } } audio { rate 48000 buffer 512 lowpass_cutoff 3390 } clocks { m68k_divider 7 max_cycles 3420 speeds { 0 100 1 150 2 200 3 300 4 400 5 25 6 50 7 75 } } ui { #specifies the ROM that implements the Menu UI rom menu.bin #starting path for ROM browsing, accepts special variables $HOME, $EXEDIR #and variables defined in the OS environment initial_path $HOME #if this is set to on, then the menu will remember the last path when visited #if it's set to off, initial_path will always be used on startup remember_path on #path for storing internal screenshots, accepts the same variables as initial_path screenshot_path $HOME #see strftime for the format specifiers valid in screenshot_template screenshot_template blastem_%Y%m%d_%H%M%S.png #path template for saving SRAM, EEPROM and savestates #accepts special variables $HOME, $EXEDIR, $USERDATA, $ROMNAME save_path $USERDATA/blastem/$ROMNAME #space delimited list of file extensions to filter against in menu extensions bin gen md smd sms gg zip gz #specifies the preferred save-state format, set to gst for Genecyst compatible states state_format native } system { #controls how the emulated system is synced to the host #video provides the smoothest experience when the host and emulated system have similar refresh rates #audio provides lower audio latency, especially when there is a refresh rate mismatch sync_source audio #set this to random to debug initialization bugs ram_init zero default_region U #controls whether MegaWiFi support is enabled or not #MegaWiFi allows ROMs to make connections to the internet #so it should only be enabled for ROMs you trust megawifi off } blastem-0.6.3.4/android/assets/images000077700000000000000000000000001374760425700212532../../imagesustar00rootroot00000000000000blastem-0.6.3.4/android/assets/menu.bin000077700000000000000000000000001374760425700217672../../menu.binustar00rootroot00000000000000blastem-0.6.3.4/android/assets/rom.db000077700000000000000000000000001374760425700211032../../rom.dbustar00rootroot00000000000000blastem-0.6.3.4/android/assets/shaders000077700000000000000000000000001374760425700216232../../shadersustar00rootroot00000000000000blastem-0.6.3.4/android/build.properties000066400000000000000000000012711374760425700201660ustar00rootroot00000000000000# This file is used to override default values used by the Ant build system. # # This file must be checked in Version Control Systems, as it is # integral to the build system of your project. # This file is only used by the Ant script. # You can use this to override default values such as # 'source.dir' for the location of your java source folder and # 'out.dir' for the location of your output folder. # You can also use it define how the release builds are signed by declaring # the following properties: # 'key.store' for the location of your keystore and # 'key.alias' for the name of the key to use. # The password will be asked during the build when you use the 'release' target. blastem-0.6.3.4/android/build.xml000066400000000000000000000076231374760425700166010ustar00rootroot00000000000000 blastem-0.6.3.4/android/default.properties000066400000000000000000000005551374760425700205170ustar00rootroot00000000000000# This file is automatically generated by Android Tools. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must be checked in Version Control Systems. # # To customize properties used by the Ant build system use, # "build.properties", and override values to adapt the script to your # project structure. # Project target. target=android-12 blastem-0.6.3.4/android/jni/000077500000000000000000000000001374760425700155305ustar00rootroot00000000000000blastem-0.6.3.4/android/jni/Android.mk000066400000000000000000000000611374760425700174360ustar00rootroot00000000000000APP_ABI=x86 include $(call all-subdir-makefiles) blastem-0.6.3.4/android/jni/Application.mk000066400000000000000000000003361374760425700203260ustar00rootroot00000000000000 # Uncomment this if you're using STL in your project # See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information # APP_STL := stlport_static APP_ABI := x86 APP_PLATFORM := android-16 APP_OPTIM := release blastem-0.6.3.4/android/jni/src000077700000000000000000000000001374760425700166062../..ustar00rootroot00000000000000blastem-0.6.3.4/android/proguard-project.txt000066400000000000000000000014151374760425700210010ustar00rootroot00000000000000# To enable ProGuard in your project, edit project.properties # to define the proguard.config property as described in that file. # # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in ${sdk.dir}/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the ProGuard # include property in project.properties. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} blastem-0.6.3.4/android/project.properties000066400000000000000000000010631374760425700205340ustar00rootroot00000000000000# This file is automatically generated by Android Tools. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must be checked in Version Control Systems. # # To customize properties used by the Ant build system edit # "ant.properties", and override values to adapt the script to your # project structure. # # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. target=android-23 blastem-0.6.3.4/android/res/000077500000000000000000000000001374760425700155415ustar00rootroot00000000000000blastem-0.6.3.4/android/res/drawable-hdpi/000077500000000000000000000000001374760425700202445ustar00rootroot00000000000000blastem-0.6.3.4/android/res/drawable-hdpi/ic_launcher.png000066400000000000000000000004621374760425700232300ustar00rootroot00000000000000‰PNG  IHDRHHUí³G pHYs$é$éP$çøäIDATxÚíÛk EáÖ¸ïÒ•ãT23ìwþ*Qn枀m¸'ûï¡Ï¼Ç‡€þÍA½RH{òžM€´¼ƒÆœÓ‚¯Örd‚TL@Ë9¨§.¾ØI&HÅ4ƒzÙ“¿qÎñáñ3×I&H@*wPî¹%ûs×d‚TL@¥<ËÏ-ÙN‰:ì4A*&  H@‚»˜»˜Š h ¥k§ônç3iÐÝ”®·‹£ç¨6¼aßÍ«˜€ÖvГüFQÅÄA?Æ5TL@¸ÒAÙÚ)Ý£ €PÀ zÜ%abIEND®B`‚blastem-0.6.3.4/android/res/drawable-mdpi/000077500000000000000000000000001374760425700202515ustar00rootroot00000000000000blastem-0.6.3.4/android/res/drawable-mdpi/ic_launcher.png000066400000000000000000000003601374760425700232320ustar00rootroot00000000000000‰PNG  IHDR00Wù‡ pHYs››Iuƒ”¢IDAThÞí˜QÀ éôÞ8yz¶$UìûΘð6 B@¡‘ŠÖ•¯ò:p ròâO&.íU±ÉŠ•r5©B« $¦À¬C©¥3mး¬¸;õô[ônPÄ@H\•«éó©1®´Ï ­ÓÝQ,ˆhP+‹;ÀØs„¸råIÉ“r€¯nð/D÷ÂÉ– ¿Ó[!d!Ç•×ô „&×xA%;¦Ž&IEND®B`‚blastem-0.6.3.4/android/res/drawable-xhdpi/000077500000000000000000000000001374760425700204345ustar00rootroot00000000000000blastem-0.6.3.4/android/res/drawable-xhdpi/ic_launcher.png000066400000000000000000000005601374760425700234170ustar00rootroot00000000000000‰PNG  IHDR``â˜w8 pHYsIÒIÒ¨EŠø"IDATxÚíÜÑ‚ FaizïðÉñ¶K`üÎu:Äi÷_³<ÞDZl½e·ýúø Î…¦ e§2S’ Ђ@X5„Û7XQnÞ¬¤´ @žÂ%ä„yÐêsÿ«f ÀÂSP™vpíÄó»ùº3n2RZ àÝ!óõ@í¶µœmï)©-€?¾Ó¯N#CtD¸Ÿ*@ €@ €ÜÇý€ÜЂ@è¡™ÛõàÚ÷Ë8@Ø,„C3÷Ö ó Õçæ ô'=-Þ:5MFž-ˆ „Ÿ̱›âɹZ […pdæNÝ/ Àþ\bÓ%‡ü*¬±IEND®B`‚blastem-0.6.3.4/android/res/drawable-xxhdpi/000077500000000000000000000000001374760425700206245ustar00rootroot00000000000000blastem-0.6.3.4/android/res/drawable-xxhdpi/ic_launcher.png000066400000000000000000000007441374760425700236130ustar00rootroot00000000000000‰PNG  IHDRçF⸠pHYsIÒIÒ¨EŠø–IDATxÚíÝk®Â €Q0wßWÎ]‰d„Ás~ÛÖ–/­·èAÆðs/ý „€QÅ/ìmŸ1@©clB@! ¢Œör÷ybóÝEú. „% ! B@”Qah}ñØüt"}€»KB@ „€¸Â7öFÙ“·¶¾Ïó,ÿÎ}ÿ«˜B@„€(£·Ó?o3›Ÿà³ùú›÷‰Ì@XÂB@ D³ÿy›™çòœÝß;÷òf „€! „€@@! â(¾¶Ê÷ÂÀ†€! jð;Ñ3±x¼ß‰! „€@@ˆ3øßøÕ‹Çæ§éì㱄! B@ˆ;ô îa¤›ï.Òpéf ,a! ¢Œþ÷8¶|Æ¥Ž±! „€@@ˆ2ºG°}«§ôšB@„€øºDd%Ñ•!ŽIEND®B`‚blastem-0.6.3.4/android/res/layout/000077500000000000000000000000001374760425700170565ustar00rootroot00000000000000blastem-0.6.3.4/android/res/layout/main.xml000066400000000000000000000006121374760425700205230ustar00rootroot00000000000000 blastem-0.6.3.4/android/res/values/000077500000000000000000000000001374760425700170405ustar00rootroot00000000000000blastem-0.6.3.4/android/res/values/strings.xml000066400000000000000000000001551374760425700212540ustar00rootroot00000000000000 BlastEm blastem-0.6.3.4/android/src/000077500000000000000000000000001374760425700155375ustar00rootroot00000000000000blastem-0.6.3.4/android/src/com/000077500000000000000000000000001374760425700163155ustar00rootroot00000000000000blastem-0.6.3.4/android/src/com/retrodev/000077500000000000000000000000001374760425700201475ustar00rootroot00000000000000blastem-0.6.3.4/android/src/com/retrodev/blastem/000077500000000000000000000000001374760425700215765ustar00rootroot00000000000000blastem-0.6.3.4/android/src/com/retrodev/blastem/BlastEmActivity.java000066400000000000000000000011251374760425700255040ustar00rootroot00000000000000package com.retrodev.blastem; import org.libsdl.app.SDLActivity; import android.os.Build; import android.os.Bundle; import android.view.View; public class BlastEmActivity extends SDLActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //set immersive mode on devices that support it if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { View blah = mSurface; blah.setSystemUiVisibility( View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY ); } } }blastem-0.6.3.4/android/src/org/000077500000000000000000000000001374760425700163265ustar00rootroot00000000000000blastem-0.6.3.4/android/src/org/libsdl/000077500000000000000000000000001374760425700175775ustar00rootroot00000000000000blastem-0.6.3.4/android/src/org/libsdl/app/000077500000000000000000000000001374760425700203575ustar00rootroot00000000000000blastem-0.6.3.4/android/src/org/libsdl/app/SDL.java000066400000000000000000000016351374760425700216510ustar00rootroot00000000000000package org.libsdl.app; import android.content.Context; /** SDL library initialization */ public class SDL { // This function should be called first and sets up the native code // so it can call into the Java classes public static void setupJNI() { SDLActivity.nativeSetupJNI(); SDLAudioManager.nativeSetupJNI(); SDLControllerManager.nativeSetupJNI(); } // This function should be called each time the activity is started public static void initialize() { setContext(null); SDLActivity.initialize(); SDLAudioManager.initialize(); SDLControllerManager.initialize(); } // This function stores the current activity (SDL or not) public static void setContext(Context context) { mContext = context; } public static Context getContext() { return mContext; } protected static Context mContext; } blastem-0.6.3.4/android/src/org/libsdl/app/SDLActivity.java000066400000000000000000001553761374760425700234020ustar00rootroot00000000000000package org.libsdl.app; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.lang.reflect.Method; import java.util.Objects; import android.app.*; import android.content.*; import android.text.InputType; import android.view.*; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.RelativeLayout; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import android.os.*; import android.util.Log; import android.util.SparseArray; import android.graphics.*; import android.graphics.drawable.Drawable; import android.hardware.*; import android.content.pm.ActivityInfo; /** SDL Activity */ public class SDLActivity extends Activity { private static final String TAG = "SDL"; public static boolean mIsResumedCalled, mIsSurfaceReady, mHasFocus; // Handle the state of the native layer public enum NativeState { INIT, RESUMED, PAUSED } public static NativeState mNextNativeState; public static NativeState mCurrentNativeState; public static boolean mExitCalledFromJava; /** If shared libraries (e.g. SDL or the native application) could not be loaded. */ public static boolean mBrokenLibraries; // If we want to separate mouse and touch events. // This is only toggled in native code when a hint is set! public static boolean mSeparateMouseAndTouch; // Main components protected static SDLActivity mSingleton; protected static SDLSurface mSurface; protected static View mTextEdit; protected static boolean mScreenKeyboardShown; protected static ViewGroup mLayout; protected static SDLClipboardHandler mClipboardHandler; // This is what SDL runs in. It invokes SDL_main(), eventually protected static Thread mSDLThread; /** * This method returns the name of the shared object with the application entry point * It can be overridden by derived classes. */ protected String getMainSharedObject() { String library; String[] libraries = SDLActivity.mSingleton.getLibraries(); if (libraries.length > 0) { library = "lib" + libraries[libraries.length - 1] + ".so"; } else { library = "libmain.so"; } return library; } /** * This method returns the name of the application entry point * It can be overridden by derived classes. */ protected String getMainFunction() { return "SDL_main"; } /** * This method is called by SDL before loading the native shared libraries. * It can be overridden to provide names of shared libraries to be loaded. * The default implementation returns the defaults. It never returns null. * An array returned by a new implementation must at least contain "SDL2". * Also keep in mind that the order the libraries are loaded may matter. * @return names of shared libraries to be loaded (e.g. "SDL2", "main"). */ protected String[] getLibraries() { return new String[] { "SDL2", // "SDL2_image", // "SDL2_mixer", // "SDL2_net", // "SDL2_ttf", "main" }; } // Load the .so public void loadLibraries() { for (String lib : getLibraries()) { System.loadLibrary(lib); } } /** * This method is called by SDL before starting the native application thread. * It can be overridden to provide the arguments after the application name. * The default implementation returns an empty array. It never returns null. * @return arguments for the native application. */ protected String[] getArguments() { return new String[0]; } public static void initialize() { // The static nature of the singleton and Android quirkyness force us to initialize everything here // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values mSingleton = null; mSurface = null; mTextEdit = null; mLayout = null; mClipboardHandler = null; mSDLThread = null; mExitCalledFromJava = false; mBrokenLibraries = false; mIsResumedCalled = false; mIsSurfaceReady = false; mHasFocus = true; mNextNativeState = NativeState.INIT; mCurrentNativeState = NativeState.INIT; } // Setup @Override protected void onCreate(Bundle savedInstanceState) { Log.v(TAG, "Device: " + android.os.Build.DEVICE); Log.v(TAG, "Model: " + android.os.Build.MODEL); Log.v(TAG, "onCreate()"); super.onCreate(savedInstanceState); // Load shared libraries String errorMsgBrokenLib = ""; try { loadLibraries(); } catch(UnsatisfiedLinkError e) { System.err.println(e.getMessage()); mBrokenLibraries = true; errorMsgBrokenLib = e.getMessage(); } catch(Exception e) { System.err.println(e.getMessage()); mBrokenLibraries = true; errorMsgBrokenLib = e.getMessage(); } if (mBrokenLibraries) { AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this); dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall." + System.getProperty("line.separator") + System.getProperty("line.separator") + "Error: " + errorMsgBrokenLib); dlgAlert.setTitle("SDL Error"); dlgAlert.setPositiveButton("Exit", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog,int id) { // if this button is clicked, close current activity SDLActivity.mSingleton.finish(); } }); dlgAlert.setCancelable(false); dlgAlert.create().show(); return; } // Set up JNI SDL.setupJNI(); // Initialize state SDL.initialize(); // So we can call stuff from static callbacks mSingleton = this; SDL.setContext(this); if (Build.VERSION.SDK_INT >= 11) { mClipboardHandler = new SDLClipboardHandler_API11(); } else { /* Before API 11, no clipboard notification (eg no SDL_CLIPBOARDUPDATE) */ mClipboardHandler = new SDLClipboardHandler_Old(); } // Set up the surface mSurface = new SDLSurface(getApplication()); mLayout = new RelativeLayout(this); mLayout.addView(mSurface); setContentView(mLayout); // Get filename from "Open with" of another application Intent intent = getIntent(); if (intent != null && intent.getData() != null) { String filename = intent.getData().getPath(); if (filename != null) { Log.v(TAG, "Got filename: " + filename); SDLActivity.onNativeDropFile(filename); } } } // Events @Override protected void onPause() { Log.v(TAG, "onPause()"); super.onPause(); mNextNativeState = NativeState.PAUSED; mIsResumedCalled = false; if (SDLActivity.mBrokenLibraries) { return; } SDLActivity.handleNativeState(); } @Override protected void onResume() { Log.v(TAG, "onResume()"); super.onResume(); mNextNativeState = NativeState.RESUMED; mIsResumedCalled = true; if (SDLActivity.mBrokenLibraries) { return; } SDLActivity.handleNativeState(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); if (SDLActivity.mBrokenLibraries) { return; } SDLActivity.mHasFocus = hasFocus; if (hasFocus) { mNextNativeState = NativeState.RESUMED; } else { mNextNativeState = NativeState.PAUSED; } SDLActivity.handleNativeState(); } @Override public void onLowMemory() { Log.v(TAG, "onLowMemory()"); super.onLowMemory(); if (SDLActivity.mBrokenLibraries) { return; } SDLActivity.nativeLowMemory(); } @Override protected void onDestroy() { Log.v(TAG, "onDestroy()"); if (SDLActivity.mBrokenLibraries) { super.onDestroy(); // Reset everything in case the user re opens the app SDLActivity.initialize(); return; } mNextNativeState = NativeState.PAUSED; SDLActivity.handleNativeState(); // Send a quit message to the application SDLActivity.mExitCalledFromJava = true; SDLActivity.nativeQuit(); // Now wait for the SDL thread to quit if (SDLActivity.mSDLThread != null) { try { SDLActivity.mSDLThread.join(); } catch(Exception e) { Log.v(TAG, "Problem stopping thread: " + e); } SDLActivity.mSDLThread = null; //Log.v(TAG, "Finished waiting for SDL thread"); } super.onDestroy(); // Reset everything in case the user re opens the app SDLActivity.initialize(); } @Override public boolean dispatchKeyEvent(KeyEvent event) { if (SDLActivity.mBrokenLibraries) { return false; } int keyCode = event.getKeyCode(); // Ignore certain special keys so they're handled by Android if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_CAMERA || keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */ keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */ ) { return false; } return super.dispatchKeyEvent(event); } /* Transition to next state */ public static void handleNativeState() { if (mNextNativeState == mCurrentNativeState) { // Already in same state, discard. return; } // Try a transition to init state if (mNextNativeState == NativeState.INIT) { mCurrentNativeState = mNextNativeState; return; } // Try a transition to paused state if (mNextNativeState == NativeState.PAUSED) { nativePause(); mSurface.handlePause(); mCurrentNativeState = mNextNativeState; return; } // Try a transition to resumed state if (mNextNativeState == NativeState.RESUMED) { if (mIsSurfaceReady && mHasFocus && mIsResumedCalled) { if (mSDLThread == null) { // This is the entry point to the C app. // Start up the C app thread and enable sensor input for the first time // FIXME: Why aren't we enabling sensor input at start? final Thread sdlThread = new Thread(new SDLMain(), "SDLThread"); mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true); sdlThread.start(); // Set up a listener thread to catch when the native thread ends mSDLThread = new Thread(new Runnable() { @Override public void run() { try { sdlThread.join(); } catch (Exception e) { // Ignore any exception } finally { // Native thread has finished if (!mExitCalledFromJava) { handleNativeExit(); } } } }, "SDLThreadListener"); mSDLThread.start(); } nativeResume(); mSurface.handleResume(); mCurrentNativeState = mNextNativeState; } } } /* The native thread has finished */ public static void handleNativeExit() { SDLActivity.mSDLThread = null; mSingleton.finish(); } // Messages from the SDLMain thread static final int COMMAND_CHANGE_TITLE = 1; static final int COMMAND_UNUSED = 2; static final int COMMAND_TEXTEDIT_HIDE = 3; static final int COMMAND_SET_KEEP_SCREEN_ON = 5; protected static final int COMMAND_USER = 0x8000; /** * This method is called by SDL if SDL did not handle a message itself. * This happens if a received message contains an unsupported command. * Method can be overwritten to handle Messages in a different class. * @param command the command of the message. * @param param the parameter of the message. May be null. * @return if the message was handled in overridden method. */ protected boolean onUnhandledMessage(int command, Object param) { return false; } /** * A Handler class for Messages from native SDL applications. * It uses current Activities as target (e.g. for the title). * static to prevent implicit references to enclosing object. */ protected static class SDLCommandHandler extends Handler { @Override public void handleMessage(Message msg) { Context context = SDL.getContext(); if (context == null) { Log.e(TAG, "error handling message, getContext() returned null"); return; } switch (msg.arg1) { case COMMAND_CHANGE_TITLE: if (context instanceof Activity) { ((Activity) context).setTitle((String)msg.obj); } else { Log.e(TAG, "error handling message, getContext() returned no Activity"); } break; case COMMAND_TEXTEDIT_HIDE: if (mTextEdit != null) { // Note: On some devices setting view to GONE creates a flicker in landscape. // Setting the View's sizes to 0 is similar to GONE but without the flicker. // The sizes will be set to useful values when the keyboard is shown again. mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0)); InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); mScreenKeyboardShown = false; } break; case COMMAND_SET_KEEP_SCREEN_ON: { if (context instanceof Activity) { Window window = ((Activity) context).getWindow(); if (window != null) { if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } else { window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } } } break; } default: if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { Log.e(TAG, "error handling message, command is " + msg.arg1); } } } } // Handler for the messages Handler commandHandler = new SDLCommandHandler(); // Send a message from the SDLMain thread boolean sendCommand(int command, Object data) { Message msg = commandHandler.obtainMessage(); msg.arg1 = command; msg.obj = data; return commandHandler.sendMessage(msg); } // C functions we call public static native int nativeSetupJNI(); public static native int nativeRunMain(String library, String function, Object arguments); public static native void nativeLowMemory(); public static native void nativeQuit(); public static native void nativePause(); public static native void nativeResume(); public static native void onNativeDropFile(String filename); public static native void onNativeResize(int x, int y, int format, float rate); public static native void onNativeKeyDown(int keycode); public static native void onNativeKeyUp(int keycode); public static native void onNativeKeyboardFocusLost(); public static native void onNativeMouse(int button, int action, float x, float y); public static native void onNativeTouch(int touchDevId, int pointerFingerId, int action, float x, float y, float p); public static native void onNativeAccel(float x, float y, float z); public static native void onNativeClipboardChanged(); public static native void onNativeSurfaceChanged(); public static native void onNativeSurfaceDestroyed(); public static native String nativeGetHint(String name); /** * This method is called by SDL using JNI. */ public static boolean setActivityTitle(String title) { // Called from SDLMain() thread and can't directly affect the view return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); } /** * This method is called by SDL using JNI. * This is a static method for JNI convenience, it calls a non-static method * so that is can be overridden */ public static void setOrientation(int w, int h, boolean resizable, String hint) { if (mSingleton != null) { mSingleton.setOrientationBis(w, h, resizable, hint); } } /** * This can be overridden */ public void setOrientationBis(int w, int h, boolean resizable, String hint) { int orientation = -1; if (!Objects.equals(hint, "")) { if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) { orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; } else if (hint.contains("LandscapeRight")) { orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; } else if (hint.contains("LandscapeLeft")) { orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; } else if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) { orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; } else if (hint.contains("Portrait")) { orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; } else if (hint.contains("PortraitUpsideDown")) { orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; } } /* no valid hint */ if (orientation == -1) { if (resizable) { /* no fixed orientation */ } else { if (w > h) { orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; } else { orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; } } } Log.v("SDL", "setOrientation() orientation=" + orientation + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint); if (orientation != -1) { mSingleton.setRequestedOrientation(orientation); } } /** * This method is called by SDL using JNI. */ public static boolean isScreenKeyboardShown() { if (mTextEdit == null) { return false; } if (!mScreenKeyboardShown) { return false; } InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); return imm.isAcceptingText(); } /** * This method is called by SDL using JNI. */ public static boolean sendMessage(int command, int param) { if (mSingleton == null) { return false; } return mSingleton.sendCommand(command, Integer.valueOf(param)); } /** * This method is called by SDL using JNI. */ public static Context getContext() { return SDL.getContext(); } static class ShowTextInputTask implements Runnable { /* * This is used to regulate the pan&scan method to have some offset from * the bottom edge of the input region and the top edge of an input * method (soft keyboard) */ static final int HEIGHT_PADDING = 15; public int x, y, w, h; public ShowTextInputTask(int x, int y, int w, int h) { this.x = x; this.y = y; this.w = w; this.h = h; } @Override public void run() { RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING); params.leftMargin = x; params.topMargin = y; if (mTextEdit == null) { mTextEdit = new DummyEdit(SDL.getContext()); mLayout.addView(mTextEdit, params); } else { mTextEdit.setLayoutParams(params); } mTextEdit.setVisibility(View.VISIBLE); mTextEdit.requestFocus(); InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(mTextEdit, 0); mScreenKeyboardShown = true; } } /** * This method is called by SDL using JNI. */ public static boolean showTextInput(int x, int y, int w, int h) { // Transfer the task to the main thread as a Runnable return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h)); } public static boolean isTextInputEvent(KeyEvent event) { // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT if (android.os.Build.VERSION.SDK_INT >= 11) { if (event.isCtrlPressed()) { return false; } } return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE; } /** * This method is called by SDL using JNI. */ public static Surface getNativeSurface() { if (SDLActivity.mSurface == null) { return null; } return SDLActivity.mSurface.getNativeSurface(); } // Input /** * This method is called by SDL using JNI. * @return an array which may be empty but is never null. */ public static int[] inputGetInputDeviceIds(int sources) { int[] ids = InputDevice.getDeviceIds(); int[] filtered = new int[ids.length]; int used = 0; for (int i = 0; i < ids.length; ++i) { InputDevice device = InputDevice.getDevice(ids[i]); if ((device != null) && ((device.getSources() & sources) != 0)) { filtered[used++] = device.getId(); } } return Arrays.copyOf(filtered, used); } // APK expansion files support /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */ private static Object expansionFile; /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */ private static Method expansionFileMethod; /** * This method is called by SDL using JNI. * @return an InputStream on success or null if no expansion file was used. * @throws IOException on errors. Message is set for the SDL error message. */ public static InputStream openAPKExpansionInputStream(String fileName) throws IOException { // Get a ZipResourceFile representing a merger of both the main and patch files if (expansionFile == null) { String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION"); if (mainHint == null) { return null; // no expansion use if no main version was set } String patchHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION"); if (patchHint == null) { return null; // no expansion use if no patch version was set } Integer mainVersion; Integer patchVersion; try { mainVersion = Integer.valueOf(mainHint); patchVersion = Integer.valueOf(patchHint); } catch (NumberFormatException ex) { ex.printStackTrace(); throw new IOException("No valid file versions set for APK expansion files", ex); } try { // To avoid direct dependency on Google APK expansion library that is // not a part of Android SDK we access it using reflection expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport") .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class) .invoke(null, SDL.getContext(), mainVersion, patchVersion); expansionFileMethod = expansionFile.getClass() .getMethod("getInputStream", String.class); } catch (Exception ex) { ex.printStackTrace(); expansionFile = null; expansionFileMethod = null; throw new IOException("Could not access APK expansion support library", ex); } } // Get an input stream for a known file inside the expansion file ZIPs InputStream fileStream; try { fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName); } catch (Exception ex) { // calling "getInputStream" failed ex.printStackTrace(); throw new IOException("Could not open stream from APK expansion file", ex); } if (fileStream == null) { // calling "getInputStream" was successful but null was returned throw new IOException("Could not find path in APK expansion file"); } return fileStream; } // Messagebox /** Result of current messagebox. Also used for blocking the calling thread. */ protected final int[] messageboxSelection = new int[1]; /** Id of current dialog. */ protected int dialogs = 0; /** * This method is called by SDL using JNI. * Shows the messagebox from UI thread and block calling thread. * buttonFlags, buttonIds and buttonTexts must have same length. * @param buttonFlags array containing flags for every button. * @param buttonIds array containing id for every button. * @param buttonTexts array containing text for every button. * @param colors null for default or array of length 5 containing colors. * @return button id or -1. */ public int messageboxShowMessageBox( final int flags, final String title, final String message, final int[] buttonFlags, final int[] buttonIds, final String[] buttonTexts, final int[] colors) { messageboxSelection[0] = -1; // sanity checks if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) { return -1; // implementation broken } // collect arguments for Dialog final Bundle args = new Bundle(); args.putInt("flags", flags); args.putString("title", title); args.putString("message", message); args.putIntArray("buttonFlags", buttonFlags); args.putIntArray("buttonIds", buttonIds); args.putStringArray("buttonTexts", buttonTexts); args.putIntArray("colors", colors); // trigger Dialog creation on UI thread runOnUiThread(new Runnable() { @Override public void run() { showDialog(dialogs++, args); } }); // block the calling thread synchronized (messageboxSelection) { try { messageboxSelection.wait(); } catch (InterruptedException ex) { ex.printStackTrace(); return -1; } } // return selected value return messageboxSelection[0]; } @Override protected Dialog onCreateDialog(int ignore, Bundle args) { // TODO set values from "flags" to messagebox dialog // get colors int[] colors = args.getIntArray("colors"); int backgroundColor; int textColor; int buttonBorderColor; int buttonBackgroundColor; int buttonSelectedColor; if (colors != null) { int i = -1; backgroundColor = colors[++i]; textColor = colors[++i]; buttonBorderColor = colors[++i]; buttonBackgroundColor = colors[++i]; buttonSelectedColor = colors[++i]; } else { backgroundColor = Color.TRANSPARENT; textColor = Color.TRANSPARENT; buttonBorderColor = Color.TRANSPARENT; buttonBackgroundColor = Color.TRANSPARENT; buttonSelectedColor = Color.TRANSPARENT; } // create dialog with title and a listener to wake up calling thread final Dialog dialog = new Dialog(this); dialog.setTitle(args.getString("title")); dialog.setCancelable(false); dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface unused) { synchronized (messageboxSelection) { messageboxSelection.notify(); } } }); // create text TextView message = new TextView(this); message.setGravity(Gravity.CENTER); message.setText(args.getString("message")); if (textColor != Color.TRANSPARENT) { message.setTextColor(textColor); } // create buttons int[] buttonFlags = args.getIntArray("buttonFlags"); int[] buttonIds = args.getIntArray("buttonIds"); String[] buttonTexts = args.getStringArray("buttonTexts"); final SparseArray