Implement interrupt handling
This commit is contained in:
		
							parent
							
								
									27bc6bcc39
								
							
						
					
					
						commit
						e94520f6cd
					
				
							
								
								
									
										41
									
								
								include/gb.h
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								include/gb.h
									
									
									
									
									
								
							|  | @ -53,28 +53,31 @@ bool gb_joypad_init(GBJoypad *joypad, RIO *io); | ||||||
| void gb_joypad_fini(GBJoypad *joypad, RIO *io); | void gb_joypad_fini(GBJoypad *joypad, RIO *io); | ||||||
| 
 | 
 | ||||||
| typedef struct gb_interrupts_t { | typedef struct gb_interrupts_t { | ||||||
|  | 	RRegItem *ime; | ||||||
| 	int fd; | 	int fd; | ||||||
| 	ut16 flags; | 	ut16 flags; | ||||||
|  | //	ut16 pc;
 | ||||||
| } GBInterrupts; | } GBInterrupts; | ||||||
| 
 | 
 | ||||||
| enum { | enum { | ||||||
| 	GB_INTERRUPTS_VBLANK = 0, | 	GB_INTERRUPT_VBLANK = 0, | ||||||
| 	GB_INTERRUPTS_STAT, | 	GB_INTERRUPT_STAT, | ||||||
| 	GB_INTERRUPTS_TIMER, | 	GB_INTERRUPT_TIMER, | ||||||
| 	GB_INTERRUPTS_SERIAL, | 	GB_INTERRUPT_SERIAL, | ||||||
| 	GB_INTERRUPTS_JOYPAD, | 	GB_INTERRUPT_JOYPAD, | ||||||
| 	GB_INTERRUPTS_N,		//number of interrupts
 | 	GB_INTERRUPT_N,		//number of interrupts
 | ||||||
| 	GB_INTERRUPTS_VBLANK_ENABLED = GB_INTERRUPTS_N, | 	GB_INTERRUPT_VBLANK_ENABLED = GB_INTERRUPT_N, | ||||||
| 	GB_INTERRUPTS_STAT_ENABLED, | 	GB_INTERRUPT_STAT_ENABLED, | ||||||
| 	GB_INTERRUPTS_TIMER_ENABLED, | 	GB_INTERRUPT_TIMER_ENABLED, | ||||||
| 	GB_INTERRUPTS_SERIAL_ENABLED, | 	GB_INTERRUPT_SERIAL_ENABLED, | ||||||
| 	GB_INTERRUPTS_JOYPAD_ENABLED, | 	GB_INTERRUPT_JOYPAD_ENABLED, | ||||||
| 	GB_INTERRUPTS_ENABLED,		//general enable of all interrupts
 | 	GB_INTERRUPT_ENABLED,		//general enable of all interrupts
 | ||||||
| 	GB_INTERRUPTS_ENABLE_WAIT,	//wait 1 instruction befor enabling interrupts
 | 	GB_INTERRUPT_ENABLE_WAIT,	//wait 1 instruction befor enabling interrupts
 | ||||||
| 	GB_INTERRUPTS_SEEK,		//2 bits for seek
 | 	GB_INTERRUPT_WAIT_TRIGGER,	//gb was put in wait state on previous call
 | ||||||
|  | 	GB_INTERRUPT_SEEK,		//2 bits for seek
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| bool gb_interrupts_init(GBInterrupts *ints, RIO *io); | bool gb_interrupts_init(GBInterrupts *ints, RIO *io, RReg *reg); | ||||||
| void gb_interrupts_fini(GBInterrupts *ints, RIO *io); | void gb_interrupts_fini(GBInterrupts *ints, RIO *io); | ||||||
| 
 | 
 | ||||||
| typedef struct gb_dma_t { | typedef struct gb_dma_t { | ||||||
|  | @ -134,6 +137,11 @@ enum { | ||||||
| 
 | 
 | ||||||
| #define	GB_PPU_STAT_MODE_MASK	0x3 | #define	GB_PPU_STAT_MODE_MASK	0x3 | ||||||
| 
 | 
 | ||||||
|  | #define	GB_PPU_STAT_INTR_HBLANK	0x8 | ||||||
|  | #define	GB_PPU_STAT_INTR_VBLANK	0x10 | ||||||
|  | #define	GB_PPU_STAT_INTR_OAM	0x20 | ||||||
|  | #define	GB_PPU_STAT_INTR_LYC	0x40 | ||||||
|  | 
 | ||||||
| typedef struct oam_scan_table { | typedef struct oam_scan_table { | ||||||
| 	ut32 data[10]; | 	ut32 data[10]; | ||||||
| 	//low byte is x coordinate of sprite;
 | 	//low byte is x coordinate of sprite;
 | ||||||
|  | @ -212,8 +220,9 @@ typedef struct gb_dmg_ppu_t { | ||||||
| 
 | 
 | ||||||
| typedef struct gameboy_t { | typedef struct gameboy_t { | ||||||
| 	RIO *io; | 	RIO *io; | ||||||
| 	REsil *esil; |  | ||||||
| 	RArch *arch; | 	RArch *arch; | ||||||
|  | 	RAnal *anal; | ||||||
|  | 	REsil *esil; | ||||||
| 	GBTimers timers; | 	GBTimers timers; | ||||||
| 	GBJoypad joypad; | 	GBJoypad joypad; | ||||||
| 	GBInterrupts interrupts; | 	GBInterrupts interrupts; | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ RIOPlugin r_io_plugin_gb_interrupts; | ||||||
| 
 | 
 | ||||||
| static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) { | static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) { | ||||||
| 	GBInterrupts *ints = desc->data; | 	GBInterrupts *ints = desc->data; | ||||||
| 	ut64 seek = (ints->flags >> GB_INTERRUPTS_SEEK) & 0x3; | 	ut64 seek = (ints->flags >> GB_INTERRUPT_SEEK) & 0x3; | ||||||
| 	switch (whence) { | 	switch (whence) { | ||||||
| 	case R_IO_SEEK_SET: | 	case R_IO_SEEK_SET: | ||||||
| 		seek = R_MIN (2, offset); | 		seek = R_MIN (2, offset); | ||||||
|  | @ -21,44 +21,44 @@ static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) { | ||||||
| 		seek = 2; | 		seek = 2; | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	ints->flags &= ~(0x3 << GB_INTERRUPTS_SEEK); | 	ints->flags &= ~(0x3 << GB_INTERRUPT_SEEK); | ||||||
| 	ints->flags |= seek << GB_INTERRUPTS_SEEK; | 	ints->flags |= seek << GB_INTERRUPT_SEEK; | ||||||
| 	return seek; | 	return seek; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) { | static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) { | ||||||
| 	GBInterrupts *ints = desc->data; | 	GBInterrupts *ints = desc->data; | ||||||
| 	ut64 seek = (ints->flags >> GB_INTERRUPTS_SEEK) & 0x3; | 	ut64 seek = (ints->flags >> GB_INTERRUPT_SEEK) & 0x3; | ||||||
| 	if (seek > 1 || len < 1) { | 	if (seek > 1 || len < 1) { | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
| 	len = R_MIN (len, 2 - seek); | 	len = R_MIN (len, 2 - seek); | ||||||
| 	ut32 i; | 	ut32 i; | ||||||
| 	for (i = 0; i < len; i++) { | 	for (i = 0; i < len; i++) { | ||||||
| 		buf[i] = (ints->flags & (0x1f << (GB_INTERRUPTS_N * seek))) >> | 		buf[i] = (ints->flags & (0x1f << (GB_INTERRUPT_N * seek))) >> | ||||||
| 			(GB_INTERRUPTS_N * seek); | 			(GB_INTERRUPT_N * seek); | ||||||
| 		seek++; | 		seek++; | ||||||
| 	} | 	} | ||||||
| 	ints->flags &= ~(0x3 << GB_INTERRUPTS_SEEK); | 	ints->flags &= ~(0x3 << GB_INTERRUPT_SEEK); | ||||||
| 	ints->flags |= seek << GB_INTERRUPTS_SEEK; | 	ints->flags |= seek << GB_INTERRUPT_SEEK; | ||||||
| 	return len; | 	return len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int __write(RIO *io, RIODesc *desc, const ut8 *buf, int len) { | static int __write(RIO *io, RIODesc *desc, const ut8 *buf, int len) { | ||||||
| 	GBInterrupts *ints = desc->data; | 	GBInterrupts *ints = desc->data; | ||||||
| 	ut64 seek = (ints->flags >> GB_INTERRUPTS_SEEK) & 0x3; | 	ut64 seek = (ints->flags >> GB_INTERRUPT_SEEK) & 0x3; | ||||||
| 	if (seek > 1 || len < 1) { | 	if (seek > 1 || len < 1) { | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
| 	len = R_MIN (len, 2 - seek); | 	len = R_MIN (len, 2 - seek); | ||||||
| 	ut32 i; | 	ut32 i; | ||||||
| 	for (i = 0; i < len; i++) { | 	for (i = 0; i < len; i++) { | ||||||
| 		ints->flags &= ~(0x1f << (GB_INTERRUPTS_N * seek)); | 		ints->flags &= ~(0x1f << (GB_INTERRUPT_N * seek)); | ||||||
| 		ints->flags |= (buf[i] & 0x1f) << (GB_INTERRUPTS_N * seek); | 		ints->flags |= (buf[i] & 0x1f) << (GB_INTERRUPT_N * seek); | ||||||
| 		seek++; | 		seek++; | ||||||
| 	} | 	} | ||||||
| 	ints->flags &= ~(0x3 << GB_INTERRUPTS_SEEK); | 	ints->flags &= ~(0x3 << GB_INTERRUPT_SEEK); | ||||||
| 	ints->flags |= seek << GB_INTERRUPTS_SEEK; | 	ints->flags |= seek << GB_INTERRUPT_SEEK; | ||||||
| 	return len; | 	return len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -96,8 +96,9 @@ RIOPlugin r_io_plugin_gb_interrupts = { | ||||||
| 	.write = __write, | 	.write = __write, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| bool gb_interrupts_init(GBInterrupts *ints, RIO *io) { | bool gb_interrupts_init(GBInterrupts *ints, RIO *io, RReg *reg) { | ||||||
| 	ints[0] = (const GBInterrupts) {0}; | 	ints[0] = (const GBInterrupts) {0}; | ||||||
|  | 	ints->ime = r_reg_get (reg, "ime", R_REG_TYPE_GPR); | ||||||
| 	char uri[64]; | 	char uri[64]; | ||||||
| 	memset (uri, 0x00, sizeof (char) * 64); | 	memset (uri, 0x00, sizeof (char) * 64); | ||||||
| 	sprintf (uri, "gb_interrupts://%p", ints); | 	sprintf (uri, "gb_interrupts://%p", ints); | ||||||
|  | @ -111,22 +112,58 @@ bool gb_interrupts_init(GBInterrupts *ints, RIO *io) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void gb_interrupts_fini(GBInterrupts *ints, RIO *io) { | void gb_interrupts_fini(GBInterrupts *ints, RIO *io) { | ||||||
|  | 	r_unref (ints->ime); | ||||||
| 	r_io_fd_close (io, ints->fd); | 	r_io_fd_close (io, ints->fd); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void gb_interrupts_continue(GB *gb) { | void gb_interrupts_continue(GB *gb) { | ||||||
| 	 | 	const bool ime = !!r_reg_get_value (gb->anal->reg, gb->interrupts.ime); | ||||||
| 	//TODO
 | 	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); | ||||||
|  | 	} 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 &= ~0xe00; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	ut32 i; | ||||||
|  | 	for (i = 0; i < GB_INTERRUPT_N; i++) { | ||||||
|  | 		const ut16 imask = (0x1 << i) | (0x1 << (i + GB_INTERRUPT_N)); | ||||||
|  | 		if ((gb->interrupts.flags & imask) == imask) { | ||||||
|  | 			goto beach; | ||||||
|  | 		} | ||||||
|  | 		//remove requests for disabled interrupts
 | ||||||
|  | 		gb->interrupts.flags &= ~(0x1 << i); | ||||||
|  | 	} | ||||||
|  | 	return; | ||||||
|  | beach: | ||||||
|  | 	if (!(gb->interrupts.flags & (0x1 << GB_INTERRUPT_WAIT_TRIGGER))) { | ||||||
|  | 		gb->interrupts.flags |= (0x1 << GB_INTERRUPT_WAIT_TRIGGER); | ||||||
|  | 		//stop exection of instructions for 20 cycles
 | ||||||
|  | 		//gb_pause_exec (gb, 20);
 | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	const ut64 interrupt_vector = 0x40 + (i << 3); | ||||||
|  | 	//maybe make this a static buffer?
 | ||||||
|  | 	char *interrupt_expr = r_str_newf ("2,sp,-=,pc,sp,=[2],0x%"PFMT64x",pc,:=", interrupt_vector); | ||||||
|  | 	r_esil_parse (gb->esil, interrupt_expr); | ||||||
|  | 	free (interrupt_expr); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void gb_interrupts_request (GB *gb, int interrupt) { | void gb_interrupts_request (GB *gb, int interrupt) { | ||||||
| 	switch (interrupt) { | 	switch (interrupt) { | ||||||
| 	case GB_INTERRUPTS_VBLANK: | 	case GB_INTERRUPT_VBLANK: | ||||||
| 	case GB_INTERRUPTS_STAT: | 	case GB_INTERRUPT_STAT: | ||||||
| 	case GB_INTERRUPTS_TIMER: | 	case GB_INTERRUPT_TIMER: | ||||||
| 	case GB_INTERRUPTS_SERIAL: | 	case GB_INTERRUPT_SERIAL: | ||||||
| 	case GB_INTERRUPTS_JOYPAD: | 	case GB_INTERRUPT_JOYPAD: | ||||||
| 		gb->interrupts.flags |= interrupt; | 		gb->interrupts.flags |= (0x1 << interrupt); | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		break; | 		break; | ||||||
|  |  | ||||||
|  | @ -135,7 +135,7 @@ void gb_joypad_continue(GB *gb) { | ||||||
| 	} | 	} | ||||||
| 	joypad->data = (joypad->data & 0x30) | (ndata ^ 0xf); | 	joypad->data = (joypad->data & 0x30) | (ndata ^ 0xf); | ||||||
| 	if (joypad->odata & ndata) { | 	if (joypad->odata & ndata) { | ||||||
| 		gb_interrupts_request (gb, GB_INTERRUPTS_JOYPAD); | 		gb_interrupts_request (gb, GB_INTERRUPT_JOYPAD); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -174,6 +174,6 @@ void gb_timers_continue (GB *gb, ut32 cycles) { | ||||||
| 		cycles -= _cycles; | 		cycles -= _cycles; | ||||||
| 	} while (cycles); | 	} while (cycles); | ||||||
| 	if (request_interrupt) { | 	if (request_interrupt) { | ||||||
| 		gb_interrupts_request (gb, GB_INTERRUPTS_TIMER); | 		gb_interrupts_request (gb, GB_INTERRUPT_TIMER); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user