dosbox-wii/src/cpu/core_dyn_x86/string.h
2021-02-06 16:06:31 +01:00

165 lines
5.1 KiB
C

/*
* Copyright (C) 2002-2019 The DOSBox Team
*
* 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 2 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
enum STRING_OP {
STR_OUTSB=0,STR_OUTSW,STR_OUTSD,
STR_INSB=4,STR_INSW,STR_INSD,
STR_MOVSB=8,STR_MOVSW,STR_MOVSD,
STR_LODSB=12,STR_LODSW,STR_LODSD,
STR_STOSB=16,STR_STOSW,STR_STOSD,
STR_SCASB=20,STR_SCASW,STR_SCASD,
STR_CMPSB=24,STR_CMPSW,STR_CMPSD
};
static void dyn_string(STRING_OP op) {
DynReg * si_base=decode.segprefix ? decode.segprefix : DREG(DS);
DynReg * di_base=DREG(ES);
DynReg * tmp_reg;bool usesi;bool usedi;
gen_protectflags();
if (decode.rep) {
gen_dop_word_imm(DOP_SUB,true,DREG(CYCLES),decode.cycles);
gen_releasereg(DREG(CYCLES));
decode.cycles=0;
}
/* Check what each string operation will be using */
switch (op) {
case STR_MOVSB: case STR_MOVSW: case STR_MOVSD:
case STR_CMPSB: case STR_CMPSW: case STR_CMPSD:
tmp_reg=DREG(TMPB);usesi=true;usedi=true;break;
case STR_LODSB: case STR_LODSW: case STR_LODSD:
tmp_reg=DREG(EAX);usesi=true;usedi=false;break;
case STR_OUTSB: case STR_OUTSW: case STR_OUTSD:
tmp_reg=DREG(TMPB);usesi=true;usedi=false;break;
case STR_SCASB: case STR_SCASW: case STR_SCASD:
case STR_STOSB: case STR_STOSW: case STR_STOSD:
tmp_reg=DREG(EAX);usesi=false;usedi=true;break;
case STR_INSB: case STR_INSW: case STR_INSD:
tmp_reg=DREG(TMPB);usesi=false;usedi=true;break;
default:
IllegalOption("dyn_string op");
}
gen_load_host(&cpu.direction,DREG(TMPW),4);
switch (op & 3) {
case 0:break;
case 1:gen_shift_word_imm(SHIFT_SHL,true,DREG(TMPW),1);break;
case 2:gen_shift_word_imm(SHIFT_SHL,true,DREG(TMPW),2);break;
default:
IllegalOption("dyn_string shift");
}
if (usesi) {
gen_preloadreg(DREG(ESI));
DynRegs[G_ESI].flags|=DYNFLG_CHANGED;
gen_preloadreg(si_base);
}
if (usedi) {
gen_preloadreg(DREG(EDI));
DynRegs[G_EDI].flags|=DYNFLG_CHANGED;
gen_preloadreg(di_base);
}
if (decode.rep) {
gen_preloadreg(DREG(ECX));
DynRegs[G_ECX].flags|=DYNFLG_CHANGED;
}
DynState rep_state;
dyn_savestate(&rep_state);
Bit8u * rep_start=cache.pos;
Bit8u * rep_ecx_jmp;
/* Check if ECX!=zero */
if (decode.rep) {
gen_dop_word(DOP_TEST,decode.big_addr,DREG(ECX),DREG(ECX));
rep_ecx_jmp=gen_create_branch_long(BR_Z);
}
if (usesi) {
if (!decode.big_addr) {
gen_extend_word(false,DREG(EA),DREG(ESI));
gen_lea(DREG(EA),si_base,DREG(EA),0,0);
} else {
gen_lea(DREG(EA),si_base,DREG(ESI),0,0);
}
switch (op&3) {
case 0:dyn_read_byte(DREG(EA),tmp_reg,false);break;
case 1:dyn_read_word(DREG(EA),tmp_reg,false);break;
case 2:dyn_read_word(DREG(EA),tmp_reg,true);break;
}
switch (op) {
case STR_OUTSB:
gen_call_function((void*)&IO_WriteB,"%Dw%Dl",DREG(EDX),tmp_reg);break;
case STR_OUTSW:
gen_call_function((void*)&IO_WriteW,"%Dw%Dw",DREG(EDX),tmp_reg);break;
case STR_OUTSD:
gen_call_function((void*)&IO_WriteD,"%Dw%Dd",DREG(EDX),tmp_reg);break;
}
}
if (usedi) {
if (!decode.big_addr) {
gen_extend_word(false,DREG(EA),DREG(EDI));
gen_lea(DREG(EA),di_base,DREG(EA),0,0);
} else {
gen_lea(DREG(EA),di_base,DREG(EDI),0,0);
}
/* Maybe something special to be done to fill the value */
switch (op) {
case STR_INSB:
gen_call_function((void*)&IO_ReadB,"%Dw%Rl",DREG(EDX),tmp_reg);
case STR_MOVSB:
case STR_STOSB:
dyn_write_byte(DREG(EA),tmp_reg,false);
break;
case STR_INSW:
gen_call_function((void*)&IO_ReadW,"%Dw%Rw",DREG(EDX),tmp_reg);
case STR_MOVSW:
case STR_STOSW:
dyn_write_word(DREG(EA),tmp_reg,false);
break;
case STR_INSD:
gen_call_function((void*)&IO_ReadD,"%Dw%Rd",DREG(EDX),tmp_reg);
case STR_MOVSD:
case STR_STOSD:
dyn_write_word(DREG(EA),tmp_reg,true);
break;
default:
IllegalOption("dyn_string op");
}
}
gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPB));
/* update registers */
if (usesi) gen_dop_word(DOP_ADD,decode.big_addr,DREG(ESI),DREG(TMPW));
if (usedi) gen_dop_word(DOP_ADD,decode.big_addr,DREG(EDI),DREG(TMPW));
if (decode.rep) {
gen_sop_word(SOP_DEC,decode.big_addr,DREG(ECX));
gen_sop_word(SOP_DEC,true,DREG(CYCLES));
gen_releasereg(DREG(CYCLES));
dyn_savestate(&save_info[used_save_info].state);
save_info[used_save_info].branch_pos=gen_create_branch_long(BR_LE);
save_info[used_save_info].eip_change=decode.op_start-decode.code_start;
save_info[used_save_info].type=normal;
used_save_info++;
/* Jump back to start of ECX check */
dyn_synchstate(&rep_state);
gen_create_jump(rep_start);
dyn_loadstate(&rep_state);
gen_fill_branch_long(rep_ecx_jmp);
}
gen_releasereg(DREG(TMPW));
}