char *get_opw_disas(struct Operand *op) {
	char *disas = (char *)malloc(LEN*sizeof(char));

	switch (op->m) {
	case 0:
		sprintf(disas, "%%R%d", op->r);
		return disas;
	case 2:
		if (op->r == 7) {
			sprintf(disas, "#%d", (*get_word_from_memory(memory.R[7])));
			return disas;
		}
	case 1: case 4:
		sprintf(disas, "0x%x", *op->address);
		return disas;
	case 3: case 5:
		sprintf(disas, "0x%x", *get_word_from_memory(*op->address));
		return disas;
	case 6:
		sprintf(disas, "0x%x", (*op->address) + (*get_word_from_memory(memory.R[7])));
		return disas;
	default:
		sprintf(disas, "0x%x", *get_word_from_memory((*op->address) + (*get_word_from_memory(memory.R[7]))));
		return disas;
	}
}
// instruction MOV
int mov(int addr, int addrs) {
	int n, z;

	put_value_w(addr, *get_word_from_memory(addrs));

	int op = *get_word_from_memory(addr);
	NWORD(n, op);
	Z(z, op);

	set_flags(n, z, 0, flags.C);

	return 0;
}
// instruction CMP
int cmp(int addr, int addrs) {
	int n, z, v, c;
	int diff;

	diff = *get_word_from_memory(addrs) - *get_word_from_memory(addr);

	VWORD(v, diff);
	CWORD(c, diff);
	NWORD(n, diff);
	Z(z, diff);

	set_flags(n, z, v, c); 

	return 0;
}
int mul(int addr, int addrs) {
	int n, z, c;
	int op;

	op = (*get_word_from_memory(addr)) * (*get_word_from_memory(addrs));

	put_value_w(addrs, (*get_word_from_memory(addr)) * (*get_word_from_memory(addrs)));

	NWORD(n, op);
	CWORD(c, op);
	Z(z, op);

	set_flags(n, z, 0, c); 

	return 0;
}
//instruction INC
int inc(int addr, int addrs) {
	int n, z, v;
	int op;

	op = *get_word_from_memory(addr) + 1;

	put_value_w(addr, *get_word_from_memory(addr) + 1);

	VWORD(v, op);
	NWORD(n, op);
	Z(z, op);
	
	set_flags(n, z, v, flags.C);

	return 0;
}
// instruction ADD
int add(int addr, int addrs) {
	int n, z, v, c;
	int op;

	op = *get_word_from_memory(addr) + *get_word_from_memory(addrs);

	put_value_w(addr, *get_word_from_memory(addr) + *get_word_from_memory(addrs));
	
	NWORD(n, op);
	VWORD(v, op);
	CWORD(c, op);
	Z(z, op);

	set_flags(n, z, v, c);

	return 0;
}
struct Operand get_rs(instruction instr) { // source
	struct Operand source;

	source.m = instr.da_instr.ms;
	source.r = instr.da_instr.rs;
	source.address = get_word_from_memory(R0_INDEX + instr.da_instr.rs * 2);

	return source;
}
struct Operand get_rd(instruction instr) { // destination
	struct Operand dest;

	dest.m = instr.sa_instr.md;
	dest.r = instr.sa_instr.rd;
	dest.address = get_word_from_memory(R0_INDEX + instr.sa_instr.rd * 2);
	
	return dest;
}
//fetches the raw integer instruction
ARM_WORD fetch_instruction(ARMSIM_CTX *ctx, ARM_ADDRESS * address) {// use the address argument to pass back the address
    as_log(ctx, "Entering fetch_instruction\n",0);
    ARM_WORD instruction = 0;
    (*address) = increment(ctx->registers, AR_r15, 4, 1);
    instruction = get_word_from_memory(ctx, *address);
    char * logstring = (char*)malloc(sizeof(char)*LOG_STRING_LENGTH);
    sprintf(logstring, "\t\tFETCH(%#010x) -> %#010x", *address, instruction);
    as_log(ctx, logstring, 0);
    free(logstring);
    as_log(ctx, "Leaving fetch_instruction\n",0);
    return instruction;
}
int get_opw(struct Operand *op) { // get address for word operand 
	int addr;

	switch (op->m) {
	case 0:
		return R0_INDEX + op->r * 2;
	case 1:
		addr = *op->address;
		return addr;
	case 2:
		if (op->r == 7) {
			addr = memory.R[7];
		} else {
			addr = *op->address;
		}
		// then we should (*op->address) += 2;
		return addr;
	case 3:
		addr = *get_word_from_memory(*op->address);
		// then we should (*op->address) += 2;
		return addr;
	case 4:
		*op->address -= 2;
		addr = *op->address;
		return addr;
	case 5:
		*op->address -= 2;
		addr = *get_word_from_memory(*op->address);
		return addr;
	case 6:
		addr = (*op->address) + (*get_word_from_memory(memory.R[7]));
		return addr;
	case 7:
		addr = *get_word_from_memory((*op->address) + (*get_word_from_memory(memory.R[7])));
		return addr;
	}

	return WRONG_ADDR;
}
void put_value_w(int address, word value) { // put word in memory
	word *dest_address;

	dest_address = get_word_from_memory(address);
	*dest_address = value;
}
int cycle(ARMSIM_CTX *ctx){
    as_log(ctx, "Entering cycle\n",0);
    if(ctx->irq != 0 && can_irq(ctx->registers)){
        unsigned int irq_ret = get_effective_pc(ctx) + 4;
        if(ctx->log_hook_defined != 0){
            as_log(ctx, "----------------------------------------------------------------------------------------", 0);
            char * string1 = (char*)malloc(sizeof(char)*LOG_STRING_LENGTH);
            sprintf(string1, "!!!! IRQ: ePC=%#010x, tPC=%#010x !!!!", get_effective_pc(ctx), get_register(ctx->registers, AR_r15));
            as_log(ctx, string1, 0);
            free(string1);
            char * string2 = (char*)malloc(sizeof(char)*LOG_STRING_LENGTH);
            char * instruction_string = instruction_to_string(master_decode(irq_ret - 4, get_word_from_memory(ctx, irq_ret - 4)));
            sprintf(string2, "???? Effective IRQ return: %s ????", instruction_string);
            as_log(ctx, string2, 0);
            free(string2);
        }
        set_mode(ctx->registers, AM_IRQ);
        set_cpsr(ctx->registers, get_cpsr(ctx->registers) | 0xC0);
        set_register(ctx->registers, AR_r14, irq_ret);
        branch_cpu(ctx, 0x00000018);
        ctx->irq = 0;

    }

    ctx-> per = as_execute_instruction(ctx, ctx->pdr);

    ctx->pdr = as_decode_instruction(ctx, ctx->pfr_address, ctx->pfr_instruction);

    ARM_ADDRESS * address = (ARM_ADDRESS*)malloc(sizeof(ARM_ADDRESS));
    ctx->pfr_instruction = fetch_instruction(ctx, address);
    ctx->pfr_address = *address;
    free(address);
    if(ctx->per != 0) {
        ctx->steps += 1;
        if (ctx->trace_hook_defined != 0) {
            cpu_trace(ctx, ctx->per);
        }
    }
    as_log(ctx, "Leaving cycle\n",0);
    return (ctx->per != 0);
}
ARMSIM_STATUS as_get_ram_word(ARMSIM_CTX *ctx, ARM_ADDRESS address, ARM_WORD *pval) {
    (*pval) = get_word_from_memory(ctx, address);
    return AS_OK;
}