/* jitx86.c Copyright (C) 2006-2007, Codemist Ltd */
/* and J O'Connell */
/*
* Just in time x86 compiler.
*
* specifically Intel/AMD x86 processing.
*/
/*
* This code may be used and modified, and redistributed in binary
* or source form, subject to the "CCL Public License", which should
* accompany it. This license is a variant on the BSD license, and thus
* permits use of code derived from this in either open and commercial
* projects: but it does require that updates to this code be made
* available back to the originators of the package.
* Before merging other code in with this or linking this code
* with other packages or libraries please check that the license terms
* of the other material are compatible with those of this.
*/
/* Signature: 4f6e2f64 18-Jan-2007 */
#include "headers.h"
#include "bytes.h"
#include "jit.h"
#define PSIZE 4 /* size of pointer */
void inc_reg(int reg)
{
if(isreg(reg)) {
putbyte(0x40+reg);
}
else aerror("Error: inc reg is not a register");
}
void dec_reg(int reg)
{
if(isreg(reg)) {
putbyte(0x48+reg);
}
else aerror("Error: dec reg is not a register");
}
void sub_imm8_rm32(int size, int reg)
{
//check if register
if(isreg(reg)) {
putbyte(0x83); //sub sign-extended imm8 from r/m32
putbyte(0xe8+reg);
putbyte(size);
}
else {
putbyte(0x83);
putbyte(0x28+reg-0x8);//FIXME is this right?
putbyte(size);
}
}
void sub_imm32_rm32(int size, int reg)
{
if(isreg(reg)) {
putbyte(0x81);
putbyte(0xe8+reg);
put_little_endian(size);
}
else {
putbyte(0x81);
putbyte(0x28+reg-0x8);
put_little_endian(size);
}
}
void add_imm8_addr(int size, void *addr)
{
put2bytes(0x8305);
put_addr(addr);
putbyte(size);
}
void sub_imm8_addr(int size, void *addr)
{
put2bytes(0x832d);
put_addr(addr);
putbyte(size);
}
/*
* below in add:
* imm8 because of special case if reg==EAX then it makes it AL.
* So instead just explicitly make me declare what size imm i want.
*
* Longer(slower?) but safer. Can optimise later
*/
void add_imm8_rm32(int size, int reg)
{
//check if register
if(isreg(reg)) {
//special case for accumulator register eax
if(reg==EAX) {
putbyte(0x04);
putbyte(size);
}
else {
putbyte(0x83);
putbyte(0xc0+reg);
putbyte(size);
}
}
//memory location
else {
putbyte(0x83);
putbyte(reg-0x8);
putbyte(size);
}
}
void add_imm32_rm32(int size, int reg)
{
//size is bigger than imm8
if(isreg(reg)) {
//special case for accumulator register eax
if(reg==EAX) {
putbyte(0x05);
put_little_endian(size);
}
else {
putbyte(0x81);
putbyte(0xc0+reg);
put_little_endian(size);
}
}
else {
putbyte(0x81);
putbyte(reg-0x8);
put_little_endian(size);
}
}
void and_imm_rm32(int size, int reg)
{
/*
* imm is ok because it is sign extended
* unlike add and sub when reg==eax
*/
//check if size is an imm8
if(0x0<=size && size <=0xff) {
//check if register
if(isreg(reg)) {
//special case for accumulator register eax
/* if(reg==EAX) {
//add imm8 to r8
putbyte(0x24);
putbyte(size);
}
else {*/
//add sign extended imm8 to r32
putbyte(0x83);
putbyte(0xe0+reg);
putbyte(size);
// }
}
//memory location
else {
putbyte(0x83);
putbyte(0x20+reg-0x8);
putbyte(size);
}
}
//size is bigger than imm8
else {
if(isreg(reg)) {
//special case for accumulator register eax
if(reg==EAX) {
putbyte(0x25);
put4bytes(size);
}
else {
putbyte(0x81);
putbyte(0xe0+reg);
put4bytes(size);
}
}
else {
putbyte(0x81);
putbyte(0x20+reg-0x8);
put4bytes(size);
}
}
}
void mov_rm32_rm32(int dest, int source)
{
//eg, "mov dest, source"
if(isreg(dest)) {
//destination is a register
putbyte(0x89);
if(isreg(source)) {
//we have a "mov %eax,%ebx"
putbyte((0xc0+(dest*0x8))+source);
}
else {
if(dest==ESP) aerror("Error: ESP in mov_rm32_rm32");
if(source==ESPM) {
putbyte(0x4+(dest*0x8));
putbyte(0x24); //SIB ESP
}
else {
//we have a "mov %eax,(%ebx)"
putbyte((dest*0x8)+source-0x8);
}
}
}
else {
//destination is a memory offset located in register
putbyte(0x8b);
if(isreg(source)) {
//we have a "mov (%eax),%ebx"
putbyte((source*0x8)+dest-0x8);
}
else {
//we have a "mov (%eax),(%ebx)"
//this is an illegal instruction
aerror("Error: in mov_rm32_rm32");
}
}
}
void mov_rm32_rm32_disp(int dest, int source, int disp)
{
//eg, "mov 0xdisp(dest), source"
//check if a disp8
if(-127<=(disp*PSIZE) && (disp*PSIZE)<=128) {
if(isreg(dest)) {
//destination is a register
putbyte(0x89);
if(isreg(source)) {
//we have a "mov %eax,%ebx"
aerror("Error: no mem loc in disp mov");
}
else {
if(dest==ESP) aerror("Error: ESP in mov_rm32_rm32_disp");
if(source==ESPM) {
putbyte(0x44+(dest*0x8));
putbyte(0x24); //SIB ESP
putbyte((PSIZE*disp)&0xff);
}
else {
//we have a "mov %eax,0xdisp(%ebx)"
putbyte(0x40+(dest*0x8)+source-0x8);
putbyte((PSIZE*disp)&0xff);
}
}
}
else {
//destination is a memory offset located in register
putbyte(0x8b);
if(isreg(source)) {
//we have a "mov 0xdisp(%eax),%ebx"
putbyte(0x40+(source*0x8)+dest-0x8);
putbyte((PSIZE*disp)&0xff);
}
else {
//we have a "mov (%eax),(%ebx)"
//this is an illegal instruction
aerror("Error: in mov_rm32_rm32_disp");
}
}
}
//its a disp32
else {
if(isreg(dest)) {
//destination is a register
putbyte(0x89);
if(isreg(source)) {
//we have a "mov %eax,%ebx"
aerror("Error: no mem loc in disp mov");
}
else {
//we have a "mov %eax,0xdisp(%ebx)"
putbyte(0x80+(dest*0x8)+source-0x8);
put_little_endian(PSIZE*disp);
}
}
else {
//destination is a memory offset located in register
putbyte(0x8b);
if(isreg(source)) {
//we have a "mov 0xdisp(%eax),%ebx"
putbyte(0x80+(source*0x8)+dest-0x8);
put_little_endian(PSIZE*disp);
}
else {
//we have a "mov (%eax),(%ebx)"
//this is an illegal instruction
aerror("Error: in mov_rm32_rm32_disp");
}
}
}
}
void cmp_imm_r32(int size, int reg)
{
//sign extended imm
if(0<=size && size<=255) {
if(isreg(reg)) {
putbyte(0x83);
putbyte(0xf8+reg);
putbyte(size);
}
else aerror("Error: not reg in cmp_imm_r32");
}
else {
//imm32
if(isreg(reg)) {
putbyte(0x81);
putbyte(0xf8+reg);
put4bytes(size);
}
else aerror("Error: not reg in cmp_imm_r32");
}
}
void cmp_rm32_rm32(int dest, int source)
{
//eg, "cmp dest, source"
if(isreg(dest)) {
//destination is a register
putbyte(0x39);
if(isreg(source)) {
//we have a "cmp %eax,%ebx"
putbyte((0xc0+(dest*0x8))+source);
}
else {
//we have a "cmp %eax,(%ebx)"
putbyte((dest*0x8)+source-0x8);
}
}
else {
//destination is a memory offset located in register
putbyte(0x3b);
if(isreg(source)) {
//we have a "cmp (%eax),%ebx"
putbyte((source*0x8)+dest-0x8);
}
else {
//we have a "cmp (%eax),(%ebx)"
//this is an illegal instruction
aerror("Error: in cmp_rm32_rm32");
}
}
}
void cmp_rm32_rm32_disp(int dest, int source, int disp)
{
//eg, "cmp 0xdisp(dest), source"
//check if a disp8
if(-127<=(disp*PSIZE) && (disp*PSIZE)<=128) {
if(isreg(dest)) {
//destination is a register
putbyte(0x39);
if(isreg(source)) {
//we have a "cmp %eax,%ebx"
aerror("Error: no mem loc in disp cmp");
}
else {
//we have a "cmp %eax,0xdisp(%ebx)"
putbyte(0x40+(dest*0x8)+source-0x8);
putbyte((PSIZE*disp)&0xff);
}
}
else {
//destination is a memory offset located in register
putbyte(0x3b);
if(isreg(source)) {
//we have a "cmp 0xdisp(%eax),%ebx"
putbyte(0x40+(source*0x8)+dest-0x8);
putbyte((PSIZE*disp)&0xff);
}
else {
//we have a "cmp (%eax),(%ebx)"
//this is an illegal instruction
aerror("Error: in cmp_rm32_rm32_disp");
}
}
}
//its a disp32
else {
if(isreg(dest)) {
//destination is a register
putbyte(0x39);
if(isreg(source)) {
//we have a "cmp %eax,%ebx"
aerror("Error: no mem loc in disp cmp");
}
else {
//we have a "cmp %eax,0xdisp(%ebx)"
putbyte(0x80+(dest*0x8)+source-0x8);
put_little_endian(PSIZE*disp);
}
}
else {
//destination is a memory offset located in register
putbyte(0x3b);
if(isreg(source)) {
//we have a "cmp 0xdisp(%eax),%ebx"
putbyte(0x80+(source*0x8)+dest-0x8);
put_little_endian(PSIZE*disp);
}
else {
//we have a "cmp (%eax),(%ebx)"
//this is an illegal instruction
aerror("Error: in cmp_rm32_rm32_disp");
}
}
}
}
void mov_r32_addr(int dest, void *source)
{
//eg, "mov %eax, 0x0" where 0x0 = C_stack
if(isreg(dest)) {
//special case for eax
if(dest==EAX) {
putbyte(0xa3);
put_addr(source);
}
else {
putbyte(0x89);
putbyte((dest*0x8)+0x5);
put_addr(source);
}
}
else aerror("Error in moving an address to a reg");
}
void mov_addr_r32(void *dest, int source)
{
//eg, "mov 0x0, %eax" where 0x0 = C_stack
if(isreg(source)) {
//special case for eax
if(source==EAX) {
putbyte(0xa1);
put_addr(dest);
}
else {
putbyte(0x8b);
putbyte((source*0x8)+0x5);
put_addr(dest);
}
}
else aerror("Error in moving a reg to an address");
}
void lea_m32_r32_disp(int source, int dest, int disp)
{
if(!isreg(source)) {
if(isreg(dest)) {
putbyte(0x8d);
if(-127<=(PSIZE*disp) && (disp*PSIZE)<=128) {
putbyte(0x40+(dest*0x8)+source-0x8);
putbyte((PSIZE*disp)&0xff);
}
else {
putbyte(0x80+(dest*0x8)+source-0x8);
put_little_endian(PSIZE*disp);
}
}
else aerror("Error in lea. dest is not a register");
}
else aerror("Error in lea. source is not a memory loc");
}
void MS_CDECL Jpush(int nargs, ...)
{
va_list arg_ptr;
int i;
if (nargs != 0)
{
va_start(arg_ptr, nargs);
for(i=0; i<nargs; i++)
{
putbyte(0x50+va_arg(arg_ptr, int));
}
va_end(arg_ptr);
}
}
void MS_CDECL Jpop(int nargs, ...)
{
va_list arg_ptr;
int i;
if (nargs != 0)
{
va_start(arg_ptr, nargs);
for(i=0; i<nargs; i++)
{
putbyte(0x58+va_arg(arg_ptr, int));
}
va_end(arg_ptr);
}
}
/* end of jitx86.c */