implement esil stop/halt ops
This commit is contained in:
		
							parent
							
								
									a7b3fa98be
								
							
						
					
					
						commit
						da49a72631
					
				
							
								
								
									
										66
									
								
								gb/esil.c
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								gb/esil.c
									
									
									
									
									
								
							|  | @ -1,11 +1,73 @@ | |||
| #include <gb.h> | ||||
| #include <r_esil.h> | ||||
| 
 | ||||
| bool gb_custom_op_halt (REsil *esil) { | ||||
| static bool gb_custom_op_halt (REsil *esil) { | ||||
| 	GB *gb = esil->user; | ||||
| 	ut64 ime = 0ULL; | ||||
| 	if (R_UNLIKELY (!r_esil_reg_read (esil, "ime", &ime, NULL))) { | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (!ime) { | ||||
| 		//pandocs says to also check for pending interrupts
 | ||||
| 		//but https://github.com/nitro2k01/little-things-gb/tree/main/double-halt-cancel doesn't mention this
 | ||||
| 		//so, wat du?
 | ||||
| 		gb->mode = GB_MODE_HALT_DOUBLE_BYTE_FETCH; | ||||
| 		return true; | ||||
| 	} | ||||
| 	if (gb->interrupts.flags & (0x1 << GB_INTERRUPT_ENABLE_WAIT)) { | ||||
| 		gb->mode = GB_MODE_HALT_ENTER; | ||||
| 		return true; | ||||
| 	} | ||||
| 	gb->mode = GB_MODE_HALT; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| bool gb_custom_op_stop (REsil *esil) { | ||||
| static bool gb_custom_op_stop (REsil *esil) { | ||||
| 	GB *gb = esil->user; | ||||
| 	//check if any button is held
 | ||||
| 	if (((gb->joypad.data & 0x30) ^ 0x30) && ((gb->joypad.data & 0x0f) ^ 0x0f)) { | ||||
| 		//check for pending interrupts
 | ||||
| 		ut32 i; | ||||
| 		for (i = 0; i < GB_INTERRUPT_N; i++) { | ||||
| 			const ut32 imask = (0x1 << i) | (0x1 << (i + GB_INTERRUPT_N)); | ||||
| 			if ((gb->interrupts.flags & imask) == imask) { | ||||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| #if 0 | ||||
| 		gb->mode = GB_MODE_HALT; | ||||
| 		return r_reg_setv (esil->anal->reg, "pc" | ||||
| 			r_reg_getv (esil->anal->reg, "pc") + 1); | ||||
| #else | ||||
| 		ut64 pc = 0ULL; | ||||
| 		if (R_UNLIKELY (!r_esil_reg_read (esil, "pc", &pc, NULL))) { | ||||
| 			return false; | ||||
| 		} | ||||
| 		pc = (pc + 1) & 0xffff; | ||||
| 		if (R_UNLIKELY (!r_esil_reg_write (esil, "pc", pc))) { | ||||
| 			return false; | ||||
| 		} | ||||
| 		gb->mode = GB_MODE_HALT; | ||||
| 		return true; | ||||
| #endif | ||||
| 	} | ||||
| 	gb->mode = GB_MODE_STOP; | ||||
| 	gb_timers_reset_div (&gb->timers); | ||||
| 	ut32 i; | ||||
| 	for (i = 0; i < GB_INTERRUPT_N; i++) { | ||||
| 		const ut32 imask = (0x1 << i) | (0x1 << (i + GB_INTERRUPT_N)); | ||||
| 		if ((gb->interrupts.flags & imask) == imask) { | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 	ut64 pc = 0ULL; | ||||
| 	if (R_UNLIKELY (!r_esil_reg_read (esil, "pc", &pc, NULL))) { | ||||
| 		return false; | ||||
| 	} | ||||
| 	pc = (pc + 1) & 0xffff; | ||||
| 	if (R_UNLIKELY (!r_esil_reg_write (esil, "pc", pc))) { | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										11
									
								
								include/gb.h
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								include/gb.h
									
									
									
									
									
								
							|  | @ -32,6 +32,7 @@ typedef struct gb_timers_t { | |||
| } GBTimers; | ||||
| 
 | ||||
| bool gb_timers_init(GBTimers *timers, RIO *io); | ||||
| void gb_timers_reset_div(GBTimers *timers); | ||||
| void gb_timers_fini(GBTimers *timers, RIO *io); | ||||
| 
 | ||||
| typedef struct gb_joypad_t { | ||||
|  | @ -72,6 +73,8 @@ enum { | |||
| 	GB_INTERRUPT_JOYPAD_ENABLED, | ||||
| 	GB_INTERRUPT_ENABLED,				//general enable of all interrupts
 | ||||
| 	GB_INTERRUPT_ENABLE_WAIT,			//wait 1 instruction befor enabling interrupts
 | ||||
| 	GB_INTERRUPT_DEC_RET,				//decrement return address when entering interrupt handler
 | ||||
| 							//to honor halt instruction bug
 | ||||
| 	GB_INTERRUPT_WAIT_TRIGGER,			//gb was put in wait state on previous call
 | ||||
| 	GB_INTERRUPT_SEEK,				//2 bits for seek
 | ||||
| 	GB_INTERRUPT_PENDING = GB_INTERRUPT_SEEK + 2,	//3 bits for pending interrupt
 | ||||
|  | @ -221,6 +224,13 @@ typedef struct gb_dmg_ppu_t { | |||
| 	ut32 oam_mapid; | ||||
| } GBPPU; | ||||
| 
 | ||||
| enum { | ||||
| 	GB_MODE_STOP, | ||||
| 	GB_MODE_HALT_ENTER, | ||||
| 	GB_MODE_HALT, | ||||
| 	GB_MODE_HALT_DOUBLE_BYTE_FETCH, | ||||
| }; | ||||
| 
 | ||||
| typedef struct gameboy_t { | ||||
| 	RIO *io; | ||||
| 	RArch *arch; | ||||
|  | @ -234,6 +244,7 @@ typedef struct gameboy_t { | |||
| 	ut64 addr; | ||||
| 	int cartrigde_fd; | ||||
| 	ut32 pause_cycles; | ||||
| 	ut32 mode; | ||||
| //	bool double_speed;
 | ||||
| } GB; | ||||
| 
 | ||||
|  |  | |||
|  | @ -118,18 +118,26 @@ void gb_interrupts_fini(GBInterrupts *ints, RIO *io) { | |||
| 
 | ||||
| void gb_interrupts_continue(GB *gb) { | ||||
| 	const bool ime = !!r_reg_get_value (gb->anal->reg, gb->interrupts.ime); | ||||
| 	bool dec_pc = false; | ||||
| 	if (gb->interrupts.flags & (0x1 << GB_INTERRUPT_ENABLE_WAIT)) { | ||||
| 		gb->interrupts.flags ^= (0x1 << GB_INTERRUPT_ENABLE_WAIT); | ||||
| 		if (!ime) { | ||||
| 			return; | ||||
| 		} | ||||
| 		gb->interrupts.flags |= (0x1 << GB_INTERRUPT_ENABLED); | ||||
| 		if (gb->mode == GB_MODE_HALT_ENTER) { | ||||
| 			dec_pc = true; | ||||
| 		} | ||||
| 	} else if (ime && !(gb->interrupts.flags & (0x1 << GB_INTERRUPT_ENABLED))) { | ||||
| 		//wait 1 instruction
 | ||||
| 		gb->interrupts.flags |= (0x1 << GB_INTERRUPT_ENABLE_WAIT); | ||||
| 		return; | ||||
| 	} else if (!ime) { | ||||
| 		gb->interrupts.flags &= 0x3ff; | ||||
| 		gb->interrupts.flags &= ~((0x1 << GB_INTERRUPT_ENABLED) | | ||||
| 			(0x1 << GB_INTERRUPT_ENABLE_WAIT) | | ||||
| 			(0x1 << GB_INTERRUPT_PENDING) | | ||||
| 			(0x1 << (GB_INTERRUPT_PENDING + 1)) | | ||||
| 			(0x1 << (GB_INTERRUPT_PENDING + 2))); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (!(gb->interrupts.flags & (0x1 << GB_INTERRUPT_WAIT_TRIGGER))) { | ||||
|  | @ -144,18 +152,24 @@ void gb_interrupts_continue(GB *gb) { | |||
| 				//save pending interrupt index
 | ||||
| 				gb->interrupts.flags &= ~(0x7 << GB_INTERRUPT_PENDING); | ||||
| 				gb->interrupts.flags |= (i << GB_INTERRUPT_PENDING); | ||||
| 				if (R_UNLIKELY (dec_pc)) { | ||||
| 					gb->interrupts.flags |= (0x1 << GB_INTERRUPT_DEC_RET); | ||||
| 				} | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| 	dec_pc = !!(gb->interrupts.flags & (0x1 << GB_INTERRUPT_DEC_RET)); | ||||
| 	gb->interrupts.flags &= ~(0x1 << GB_INTERRUPT_DEC_RET); | ||||
| 	gb->interrupts.flags ^= 0x1 << GB_INTERRUPT_WAIT_TRIGGER; | ||||
| 	const ut32 interrupt = (gb->interrupts.flags & (0x7 << GB_INTERRUPT_PENDING)) >> GB_INTERRUPT_PENDING; | ||||
| 	gb->interrupts.flags &= ~(0x1 << interrupt); | ||||
| 	const ut64 interrupt_vector = 0x40 + (interrupt << 3); | ||||
| 	//maybe make this a static buffer?
 | ||||
| 	char *interrupt_expr = r_str_newf ( | ||||
| 		"0,ime,:=,2,sp,-=,pc,sp,=[2],0x%"PFMT64x",pc,:=", interrupt_vector); | ||||
| 	char *interrupt_expr = r_str_newf (dec_pc ? | ||||
| 		"0,ime,:=,2,sp,-=,1,pc,-,sp,=[2],0x%"PFMT64x",pc,;=" | ||||
| 		:"0,ime,:=,2,sp,-=,pc,sp,=[2],0x%"PFMT64x",pc,:=", interrupt_vector); | ||||
| 	r_esil_parse (gb->esil, interrupt_expr); | ||||
| 	free (interrupt_expr); | ||||
| } | ||||
|  |  | |||
|  | @ -104,7 +104,7 @@ bool gb_joypad_init (GBJoypad *joypad, RIO *io) { | |||
| 	joypad->b = SDL_SCANCODE_K; | ||||
| 	joypad->start = SDL_SCANCODE_SPACE; | ||||
| 	joypad->select = SDL_SCANCODE_KP_ENTER; | ||||
| 	return true;; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void gb_joypad_continue(GB *gb) { | ||||
|  | @ -113,7 +113,7 @@ void gb_joypad_continue(GB *gb) { | |||
| 	joypad->odata |= joypad->data & 0x3f; | ||||
| 	SDL_PumpEvents (); | ||||
| 	ut8 ndata = 0; | ||||
| 	if (!(joypad->data & 0x8)) {	//d-pad
 | ||||
| 	if (!(joypad->data & 0x10)) {	//d-pad
 | ||||
| 		const ut8 data = (~joypad->data) & 0xf; | ||||
| 		ndata = !!joypad->keys[joypad->right]; | ||||
| 		ndata |= (!!joypad->keys[joypad->left]) << 1; | ||||
|  | @ -127,7 +127,7 @@ void gb_joypad_continue(GB *gb) { | |||
| 			ndata = (ndata & 0x3) | (data & 0xc); | ||||
| 		} | ||||
| 	} | ||||
| 	if (!(joypad->data & 0x10)) {	//buttons
 | ||||
| 	if (!(joypad->data & 0x20)) {	//buttons
 | ||||
| 		ndata |= !!joypad->keys[joypad->a]; | ||||
| 		ndata |= (!!joypad->keys[joypad->b]) << 1; | ||||
| 		ndata |= (!!joypad->keys[joypad->select]) << 2; | ||||
|  |  | |||
|  | @ -177,3 +177,8 @@ void gb_timers_continue (GB *gb, ut32 cycles) { | |||
| 		gb_interrupts_request (gb, GB_INTERRUPT_TIMER); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void gb_timers_reset_div (GBTimers *timers) { | ||||
| 	timers->buf[GB_TIMERS_DIV] = 0; | ||||
| 	timers->div = 0; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user