changeset 2577:5f725429d08f

WIP changes to new CPU core for rotate instructions and to get interrupts more functional
author Michael Pavone <pavone@retrodev.com>
date Fri, 07 Feb 2025 08:57:24 -0800
parents c9bfed9156dc
children 9b01541cbd60
files cpu_dsl.py genesis.c m68k.cpu m68k_util.c
diffstat 4 files changed, 558 insertions(+), 104 deletions(-) [+]
line wrap: on
line diff
--- a/cpu_dsl.py	Mon Feb 03 22:28:20 2025 -0800
+++ b/cpu_dsl.py	Fri Feb 07 08:57:24 2025 -0800
@@ -482,6 +482,22 @@
 	else:
 		raise Exception('Unsupported dispatch type ' + prog.dispatch)
 
+def _addExplicitFlagSet(prog, output, flag, flagval):
+	location = prog.flags.getStorage(flag)
+	if type(location) is tuple:
+		reg,bit = location
+		reg = prog.resolveReg(reg, None, {})
+		value = str(1 << bit)
+		if flagval:
+			operator = '|='
+		else:
+			operator = '&='
+			value = '~' + value
+		output.append('\n\t{reg} {op} {val};'.format(reg=reg, op=operator, val=value))
+	else:
+		reg = prog.resolveReg(location, None, {})
+		output.append('\n\t{reg} = {val};'.format(reg=reg, val=flagval))
+
 def _updateFlagsCImpl(prog, params, rawParams):
 	autoUpdate, explicit = prog.flags.parseFlagUpdate(params[0])
 	output = []
@@ -497,15 +513,60 @@
 		storage = prog.flags.getStorage(flag)
 		if calc == 'bit' or calc == 'sign' or calc == 'carry' or calc == 'half' or calc == 'overflow':
 			myRes = lastDst
+			after = ''
 			if calc == 'sign':
 				resultBit = prog.getLastSize() - 1
 			elif calc == 'carry':
-				if prog.lastOp.op in ('asr', 'lsr'):
+				if prog.lastOp.op in ('asr', 'lsr', 'rrc'):
 					if type(prog.lastB) is int:
-						resultBit = prog.lastB - 1
+						if prog.lastB == 0:
+							explicit[flag] = 0
+							continue
+						else:
+							resultBit = prog.lastB - 1
 					else:
+						output.append(f'\n\tif (!{prog.lastB}) {{')
+						_addExplicitFlagSet(prog, output, flag, 0)
+						output.append('\n\t} else {')
+						after = '\n\t}'
 						resultBit = f'({prog.lastB} - 1)'
 					myRes = prog.lastA
+				elif prog.lastOp.op == 'rlc':
+					if type(prog.lastB) is int:
+						if prog.lastB == 0:
+							explicit[flag] = 0
+							continue
+						else:
+							resultBit = prog.getLastSize() - prog.lastB
+					else:
+						output.append(f'\n\tif (!{prog.lastB}) {{')
+						_addExplicitFlagSet(prog, output, flag, 0)
+						output.append('\n\t} else {')
+						after = '\n\t}'
+						resultBit = f'({prog.getLastSize()} - {prog.lastB})'
+					myRes = prog.lastA
+				elif prog.lastOp.op == 'rol':
+					if type(prog.lastB) is int:
+						if prog.lastB == 0:
+							explicit[flag] = 0
+							continue
+					else:
+						output.append(f'\n\tif (!{prog.lastB}) {{')
+						_addExplicitFlagSet(prog, output, flag, 0)
+						output.append('\n\t} else {')
+						after = '\n\t}'
+					resultBit = 0
+				elif prog.lastOp.op == 'ror':
+					if type(prog.lastB) is int:
+						if prog.lastB == 0:
+							explicit[flag] = 0
+							continue
+					else:
+						output.append(f'\n\tif (!{prog.lastB}) {{')
+						_addExplicitFlagSet(prog, output, flag, 0)
+						output.append('\n\t} else {')
+						after = '\n\t}'
+					resultBit = prog.getLastSize() - 1
 				elif prog.lastOp.op == 'neg':
 					if prog.carryFlowDst:
 						realSize = prog.getLastSize()
@@ -525,8 +586,6 @@
 					continue
 				else:
 					resultBit = prog.getLastSize()
-					if prog.lastOp.op == 'ror':
-						resultBit -= 1
 			elif calc == 'half':
 				resultBit = prog.getLastSize() - 4
 				myRes = '({a} ^ {b} ^ {res})'.format(a = prog.lastA, b = prog.lastB, res = lastDst)
@@ -566,6 +625,8 @@
 					output.append('\n\t{reg} = {res} >> {shift} & {mask};'.format(reg=reg, res=myRes, shift = resultBit - maxBit, mask = mask))
 				else:
 					output.append('\n\t{reg} = {res} & {mask};'.format(reg=reg, res=myRes, mask = mask))
+			if after:
+				output.append(after)
 		elif calc == 'zero':
 			if prog.carryFlowDst:
 				realSize = prog.getLastSize()
@@ -636,20 +697,7 @@
 			
 	#TODO: combine explicit flags targeting the same storage location
 	for flag in explicit:
-		location = prog.flags.getStorage(flag)
-		if type(location) is tuple:
-			reg,bit = location
-			reg = prog.resolveReg(reg, None, {})
-			value = str(1 << bit)
-			if explicit[flag]:
-				operator = '|='
-			else:
-				operator = '&='
-				value = '~' + value
-			output.append('\n\t{reg} {op} {val};'.format(reg=reg, op=operator, val=value))
-		else:
-			reg = prog.resolveReg(location, None, {})
-			output.append('\n\t{reg} = {val};'.format(reg=reg, val=explicit[flag]))
+		_addExplicitFlagSet(prog, output, flag, explicit[flag])
 	return ''.join(output)
 	
 def _cmpCImpl(prog, params, rawParams, flagUpdates):
@@ -929,15 +977,37 @@
 			if calc == 'carry':
 				needsCarry = True
 	decl = ''
-	size = prog.paramSize(rawParams[2])
-	if needsCarry:
-		decl,name = prog.getTemp(size * 2)
+	destSize = prog.paramSize(rawParams[2])
+	needsSizeAdjust = False
+	if len(params) > 3:
+		size = params[3]
+		if size == 0:
+			size = 8
+		elif size == 1:
+			size = 16
+		else:
+			size = 32
+		prog.lastSize = size
+		if destSize > size:
+			needsSizeAdjust = True
+			if needsCarry:
+				prog.sizeAdjust = size
+	else:
+		size = destSize
+	
+	prog.lastB = params[1]
+	if needsSizeAdjust:
+		decl,name = prog.getTemp(size)
 		dst = prog.carryFlowDst = name
 	else:
 		dst = params[2]
-	return decl + '\n\t{dst} = {a} << {b} | {a} >> ({size} - {b});'.format(dst = dst,
+	ret = decl + '\n\t{dst} = {a} << {b} | {a} >> ({size} - {b});'.format(dst = dst,
 		a = params[0], b = params[1], size=size
 	)
+	if needsSizeAdjust and not needsCarry:
+		mask = (1 << size) - 1
+		ret += f'\n\t{params[2]} = ({params[2]} & ~{mask}) | ({dst} & {mask});'
+	return ret
 	
 def _rlcCImpl(prog, params, rawParams, flagUpdates):
 	needsCarry = False
@@ -947,22 +1017,83 @@
 			if calc == 'carry':
 				needsCarry = True
 	decl = ''
+	destSize = prog.paramSize(rawParams[2])
+	needsSizeAdjust = False
+	if len(params) > 3:
+		size = params[3]
+		if size == 0:
+			size = 8
+		elif size == 1:
+			size = 16
+		else:
+			size = 32
+		prog.lastSize = size
+		if destSize > size:
+			needsSizeAdjust = True
+			if needsCarry:
+				prog.sizeAdjust = size
+	else:
+		size = destSize
 	carryCheck = _getCarryCheck(prog)
 	size = prog.paramSize(rawParams[2])
-	if needsCarry:
-		decl,name = prog.getTemp(size * 2)
+	if needsCarry or needsSizeAdjust:
+		decl,name = prog.getTemp(size)
+		dst = prog.carryFlowDst = name
+		prog.lastA = params[0]
+		prog.lastB = params[1]
+	else:
+		dst = params[2]
+	if size == 32 and (not type(params[1]) is int) or params[1] <= 1:
+		# we may need to shift by 32-bits which is too much for a normal int
+		a = f'((uint64_t){params[0]})'
+	else:
+		a = params[0]
+	ret = decl + '\n\t{dst} = {a} << {b} | {a} >> ({size} + 1 - {b}) | ({check} ? 1 : 0) << ({b} - 1);'.format(dst = dst,
+		a = a, b = params[1], size=size, check=carryCheck
+	)
+	if needsSizeAdjust and not needsCarry:
+		mask = (1 << size) - 1
+		ret += f'\n\t{params[2]} = ({params[2]} & ~{mask}) | ({dst} & {mask});'
+	return ret
+	
+def _rorCImpl(prog, params, rawParams, flagUpdates):
+	needsCarry = False
+	if flagUpdates:
+		for flag in flagUpdates:
+			calc = prog.flags.flagCalc[flag]
+			if calc == 'carry':
+				needsCarry = True
+	decl = ''
+	destSize = prog.paramSize(rawParams[2])
+	needsSizeAdjust = False
+	if len(params) > 3:
+		size = params[3]
+		if size == 0:
+			size = 8
+		elif size == 1:
+			size = 16
+		else:
+			size = 32
+		prog.lastSize = size
+		if destSize > size:
+			needsSizeAdjust = True
+			if needsCarry:
+				prog.sizeAdjust = size
+	else:
+		size = destSize
+	prog.lastB = params[1]
+	if needsSizeAdjust:
+		decl,name = prog.getTemp(size)
 		dst = prog.carryFlowDst = name
 	else:
 		dst = params[2]
-	return decl + '\n\t{dst} = {a} << {b} | {a} >> ({size} + 1 - {b}) | ({check} ? 1 : 0) << ({b} - 1);'.format(dst = dst,
-		a = params[0], b = params[1], size=size, check=carryCheck
-	)
-	
-def _rorCImpl(prog, params, rawParams, flagUpdates):
-	size = prog.paramSize(rawParams[2])
-	return '\n\t{dst} = {a} >> {b} | {a} << ({size} - {b});'.format(dst = params[2],
+	ret = decl + '\n\t{dst} = {a} >> {b} | {a} << ({size} - {b});'.format(dst = dst,
 		a = params[0], b = params[1], size=size
 	)
+	if needsSizeAdjust and not needsCarry:
+		mask = (1 << size) - 1
+		ret += f'\n\t{params[2]} = ({params[2]} & ~{mask}) | ({dst} & {mask});'
+	return ret
 
 def _rrcCImpl(prog, params, rawParams, flagUpdates):
 	needsCarry = False
@@ -972,16 +1103,44 @@
 			if calc == 'carry':
 				needsCarry = True
 	decl = ''
+	destSize = prog.paramSize(rawParams[2])
+	needsSizeAdjust = False
+	if len(params) > 3:
+		size = params[3]
+		if size == 0:
+			size = 8
+		elif size == 1:
+			size = 16
+		else:
+			size = 32
+		prog.lastSize = size
+		if destSize > size:
+			needsSizeAdjust = True
+			if needsCarry:
+				prog.sizeAdjust = size
+	else:
+		size = destSize
 	carryCheck = _getCarryCheck(prog)
 	size = prog.paramSize(rawParams[2])
-	if needsCarry:
-		decl,name = prog.getTemp(size * 2)
+	if needsCarry or needsSizeAdjust:
+		decl,name = prog.getTemp(size)
 		dst = prog.carryFlowDst = name
+		prog.lastA = params[0]
+		prog.lastB = params[1]
 	else:
 		dst = params[2]
-	return decl + '\n\t{dst} = {a} >> {b} | {a} << ({size} + 1 - {b}) | ({check} ? 1 : 0) << ({size}-{b});'.format(dst = dst,
-		a = params[0], b = params[1], size=size, check=carryCheck
+	if size == 32 and (not type(params[1]) is int) or params[1] <= 1:
+		# we may need to shift by 32-bits which is too much for a normal int
+		a = f'((uint64_t){params[0]})'
+	else:
+		a = params[0]
+	ret = decl + '\n\t{dst} = {a} >> {b} | {a} << ({size} + 1 - {b}) | ({check} ? 1 : 0) << ({size}-{b});'.format(dst = dst,
+		a = a, b = params[1], size=size, check=carryCheck
 	)
+	if needsSizeAdjust and not needsCarry:
+		mask = (1 << size) - 1
+		ret += f'\n\t{params[2]} = ({params[2]} & ~{mask}) | ({dst} & {mask});'
+	return ret
 	
 def _updateSyncCImpl(prog, params):
 	return '\n\t{sync}(context, target_cycle);'.format(sync=prog.sync_cycle)
@@ -1683,20 +1842,22 @@
 		hFile.write('\n#define {0}_'.format(macro))
 		hFile.write('\n#include <stdio.h>')
 		hFile.write('\n#include "backend.h"')
-		hFile.write('\n\ntypedef struct {')
-		hFile.write('\n\tcpu_options gen;')
-		hFile.write('\n\tFILE* address_log;')
-		hFile.write('\n}} {0}options;'.format(self.prefix))
-		hFile.write('\n\ntypedef struct {')
-		hFile.write('\n\t{0}options *opts;'.format(self.prefix))
-		self.regs.writeHeader(otype, hFile)
-		hFile.write('\n}} {0}context;'.format(self.prefix))
-		hFile.write('\n')
-		hFile.write('\nvoid {pre}execute({type} *context, uint32_t target_cycle);'.format(pre = self.prefix, type = self.context_type))
+		hFile.write(f'\n\ntypedef struct {self.prefix}options {self.prefix}options;')
+		hFile.write(f'\n\ntypedef struct {self.prefix}context {self.prefix}context;')
 		for decl in self.declares:
 			if decl.startswith('define '):
 				decl = '#' + decl
 			hFile.write('\n' + decl)
+		hFile.write(f'\n\nstruct {self.prefix}options {{')
+		hFile.write('\n\tcpu_options gen;')
+		hFile.write('\n\tFILE* address_log;')
+		hFile.write('\n};')
+		hFile.write(f'\n\nstruct {self.prefix}context {{')
+		hFile.write(f'\n\t{self.prefix}options *opts;')
+		self.regs.writeHeader(otype, hFile)
+		hFile.write('\n};')
+		hFile.write('\n')
+		hFile.write('\nvoid {pre}execute({type} *context, uint32_t target_cycle);'.format(pre = self.prefix, type = self.context_type))
 		hFile.write('\n#endif //{0}_'.format(macro))
 		hFile.write('\n')
 		hFile.close()
--- a/genesis.c	Mon Feb 03 22:28:20 2025 -0800
+++ b/genesis.c	Fri Feb 07 08:57:24 2025 -0800
@@ -46,6 +46,7 @@
 #define Z80_CYCLE cycles
 #define Z80_OPTS opts
 #define z80_handle_code_write(...)
+#define int_num int_priority
 #else
 #define Z80_CYCLE current_cycle
 #define Z80_OPTS options
@@ -698,6 +699,11 @@
 			context->sync_cycle = context->cycles + 1;
 		}
 	}
+#ifdef NEW_CORE
+	if (context->target_cycle == context->cycles) {
+		context->target_cycle++;
+	}
+#endif
 	return context;
 }
 
--- a/m68k.cpu	Mon Feb 03 22:28:20 2025 -0800
+++ b/m68k.cpu	Fri Feb 07 08:57:24 2025 -0800
@@ -8,11 +8,11 @@
 	sync_cycle m68k_sync_cycle
 	
 declare
-	typedef m68k_context *(*sync_fun)(m68k_context * context, uint32_t address);
+	typedef m68k_context *(sync_fun)(m68k_context * context, uint32_t address);
 	typedef m68k_context *(*int_ack_fun)(m68k_context * context);
 	typedef m68k_context *(*m68k_reset_handler)(m68k_context *context);
-	void init_m68k_opts(m68k_options *opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider, sync_fun sync_components, int_ack_fun int_ack);
-	m68k_context *init_68k_context(m68k_options * opts, m68k_reset_handler reset_handler);
+	void init_m68k_opts(m68k_options *opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider, sync_fun *sync_components, int_ack_fun int_ack);
+	m68k_context *init_68k_context(m68k_options * opts, m68k_reset_handler *reset_handler);
 	void m68k_reset(m68k_context *context);
 	void m68k_print_regs(m68k_context *context);
 	void m68k_serialize(m68k_context *context, uint32_t pc, serialize_buffer *buf);
@@ -62,6 +62,8 @@
 	should_return 8
 	system ptrvoid
 	reset_handler ptrvoid
+	int_ack_handler ptrvoid
+	sync_components ptrsync_fun
 	mem_pointers ptr16 10
 	
 flags
@@ -158,59 +160,56 @@
 	
 	
 m68k_interrupt
-	cmp int_cycle cycles
-	if >=U
-	
-	#INT_PENDING_NONE
-	cmp 255 int_pending
-	if =
-	
-	mov int_priority int_pending
-	mov int_num int_pending_num
-	
-	else
-	
-	#INT_PENDING_SR_CHANGE
-	cmp 254 int_pending
-	if =
-	
-	mov int_priority int_pending
-	mov int_num int_pending_num
-	
-	else
-	
-	check_user_mode_swap_ssp_usp
+	if cycles >=U int_cycle
 	
-	cycles 6
-	#save status reg
-	sub 6 a7 a7
-	m68k_get_sr
-	mov a7 scratch2
-	ocall write_16
-	
-	#update status register
-	and 0x78 status status
-	or int_priority status status
-	or 0x20 status status
-	
-	#Interrupt ack cycle
-	mov int_pending int_ack
-	if int_pending_num
-	cycles 4
-	else
-	#TODO: do the whole E clock variable latency nonsense
-	cycles 13
-	add 24 int_pending int_pending_num
-	end
-	
-	#save pc
-	add 2 a7 scratch2
-	m68k_write32_lowfirst pc
-	
-	lsl int_pending_num 2 scratch1
-	m68k_read32
-	mov scratch1 pc
-	update_sync
+		#INT_PENDING_NONE
+		if 255 = int_pending
+			int_pending = int_priority
+			int_pending_num = int_num
+		else
+		
+			#INT_PENDING_SR_CHANGE
+			if 254 = int_pending
+				int_pending = int_priority
+				int_pending_num = int_num
+			
+			else
+			
+				check_user_mode_swap_ssp_usp
+				
+				cycles 6
+				#save status reg
+				sub 6 a7 a7
+				m68k_get_sr
+				mov a7 scratch2
+				ocall write_16
+				
+				#update status register
+				and 0x78 status status
+				or int_priority status status
+				or 0x20 status status
+				
+				#Interrupt ack cycle
+				mov int_pending int_ack
+				cycles 4
+				if int_ack_handler
+					pcall int_ack_handler int_ack_fun context
+				end
+				if int_pending_num
+				else
+					int_pending_num = int_pending + 24
+				end
+				
+				#save pc
+				add 2 a7 scratch2
+				m68k_write32_lowfirst pc
+				
+				lsl int_pending_num 2 scratch1
+				m68k_read32
+				mov scratch1 pc
+				update_sync
+			end
+		end
 	end
 	
 m68k_run_op
@@ -2562,3 +2561,283 @@
 		end
 	end
 	m68k_prefetch
+
+1110CCC0ZZ011RRR rori
+	invalid Z 3
+	switch C
+	case 0
+		meta shift 8
+	default
+		meta shift C
+	end
+	ror dregs.R shift dregs.R Z
+	update_flags NZV0C
+	local cyc 32
+	cyc = shift + shift
+	switch Z
+	case 2
+		cyc += 4
+	default
+		cyc += 2
+	end
+	cycles cyc
+	#TODO: should this happen before or after the majority of the rotate?
+	m68k_prefetch
+
+1110CCC0ZZ111RRR ror_dn
+	invalid Z 3
+	local shift 8
+	local cycle_shift 8
+	cycle_shift = dregs.C & 63
+	switch Z
+	case 2
+		if cycle_shift = 32
+			ror dregs.R 31 dregs.R Z
+			ror dregs.R 1 dregs.R Z
+			update_flags NZV0C
+		else
+			shift = dregs.C & 31
+			ror dregs.R shift dregs.R Z
+			update_flags NZV0C
+		end
+	default
+		shift = dregs.C & 31
+		ror dregs.R shift dregs.R Z
+		update_flags NZV0C
+	end
+	cycle_shift += cycle_shift
+	switch Z
+	case 2
+		cycle_shift += 4
+	default
+		cycle_shift += 2
+	end
+	cycles cycle_shift
+	#TODO: should this happen before or after the majority of the rotate?
+	m68k_prefetch
+
+1110011011MMMRRR ror_ea
+	invalid M 0
+	invalid M 1
+	invalid M 7 R 2
+	invalid M 7 R 3
+	invalid M 7 R 4
+	invalid M 7 R 5
+	invalid M 7 R 6
+	invalid M 7 R 7
+	
+	m68k_fetch_dst_ea M R 1
+	ror dst 1 dst 1
+	update_flags XNZV0C
+	m68k_save_dst 1
+	m68k_prefetch
+
+1110CCC1ZZ011RRR roli
+	invalid Z 3
+	switch C
+	case 0
+		meta shift 8
+	default
+		meta shift C
+	end
+	rol dregs.R shift dregs.R Z
+	update_flags NZV0C
+	local cyc 32
+	cyc = shift + shift
+	switch Z
+	case 2
+		cyc += 4
+	default
+		cyc += 2
+	end
+	cycles cyc
+	#TODO: should this happen before or after the majority of the rotate?
+	m68k_prefetch
+
+1110CCC1ZZ111RRR rol_dn
+	invalid Z 3
+	local shift 8
+	local cycle_shift 8
+	cycle_shift = dregs.C & 63
+	switch Z
+	case 2
+		if cycle_shift = 32
+			rol dregs.R 31 dregs.R Z
+			rol dregs.R 1 dregs.R Z
+			update_flags NZV0C
+		else
+			shift = dregs.C & 31
+			rol dregs.R shift dregs.R Z
+			update_flags NZV0C
+		end
+	default
+		shift = dregs.C & 31
+		rol dregs.R shift dregs.R Z
+		update_flags NZV0C
+	end
+	cycle_shift += cycle_shift
+	switch Z
+	case 2
+		cycle_shift += 4
+	default
+		cycle_shift += 2
+	end
+	cycles cycle_shift
+	#TODO: should this happen before or after the majority of the rotate?
+	m68k_prefetch
+
+1110011111MMMRRR rol_ea
+	invalid M 0
+	invalid M 1
+	invalid M 7 R 2
+	invalid M 7 R 3
+	invalid M 7 R 4
+	invalid M 7 R 5
+	invalid M 7 R 6
+	invalid M 7 R 7
+	
+	m68k_fetch_dst_ea M R 1
+	rol dst 1 dst 1
+	update_flags NZV0C
+	m68k_save_dst 1
+	m68k_prefetch
+
+1110CCC0ZZ010RRR roxri
+	invalid Z 3
+	switch C
+	case 0
+		meta shift 8
+	default
+		meta shift C
+	end
+	rrc dregs.R shift dregs.R Z
+	update_flags XNZV0C
+	local cyc 32
+	cyc = shift + shift
+	switch Z
+	case 2
+		cyc += 4
+	default
+		cyc += 2
+	end
+	cycles cyc
+	#TODO: should this happen before or after the majority of the rotate?
+	m68k_prefetch
+
+1110CCC0ZZ110RRR roxr_dn
+	invalid Z 3
+	local shift 8
+	local cycle_shift 8
+	cycle_shift = dregs.C & 63
+	switch Z
+	case 2
+		if cycle_shift = 32
+			rrc dregs.R 31 dregs.R Z
+			rrc dregs.R 1 dregs.R Z
+			update_flags NZV0C
+		else
+			shift = dregs.C & 31
+			rrc dregs.R shift dregs.R Z
+			update_flags NZV0C
+		end
+	default
+		shift = dregs.C & 31
+		rrc dregs.R shift dregs.R Z
+		update_flags XNZV0C
+	end
+	cycle_shift += cycle_shift
+	switch Z
+	case 2
+		cycle_shift += 4
+	default
+		cycle_shift += 2
+	end
+	cycles cycle_shift
+	#TODO: should this happen before or after the majority of the rotate?
+	m68k_prefetch
+
+1110010011MMMRRR roxr_ea
+	invalid M 0
+	invalid M 1
+	invalid M 7 R 2
+	invalid M 7 R 3
+	invalid M 7 R 4
+	invalid M 7 R 5
+	invalid M 7 R 6
+	invalid M 7 R 7
+	
+	m68k_fetch_dst_ea M R 1
+	rrc dst 1 dst 1
+	update_flags XNZV0C
+	m68k_save_dst 1
+	m68k_prefetch
+
+1110CCC1ZZ010RRR roxli
+	invalid Z 3
+	switch C
+	case 0
+		meta shift 8
+	default
+		meta shift C
+	end
+	rlc dregs.R shift dregs.R Z
+	update_flags XNZV0C
+	local cyc 32
+	cyc = shift + shift
+	switch Z
+	case 2
+		cyc += 4
+	default
+		cyc += 2
+	end
+	cycles cyc
+	#TODO: should this happen before or after the majority of the rotate?
+	m68k_prefetch
+
+1110CCC1ZZ110RRR roxl_dn
+	invalid Z 3
+	local shift 8
+	local cycle_shift 8
+	cycle_shift = dregs.C & 63
+	switch Z
+	case 2
+		if cycle_shift = 32
+			rrc dregs.R 31 dregs.R Z
+			rlc dregs.R 1 dregs.R Z
+			update_flags NZV0C
+		else
+			shift = dregs.C & 31
+			rlc dregs.R shift dregs.R Z
+			update_flags NZV0C
+		end
+	default
+		shift = dregs.C & 31
+		rlc dregs.R shift dregs.R Z
+		update_flags XNZV0C
+	end
+	cycle_shift += cycle_shift
+	switch Z
+	case 2
+		cycle_shift += 4
+	default
+		cycle_shift += 2
+	end
+	cycles cycle_shift
+	#TODO: should this happen before or after the majority of the rotate?
+	m68k_prefetch
+
+1110010111MMMRRR roxl_ea
+	invalid M 0
+	invalid M 1
+	invalid M 7 R 2
+	invalid M 7 R 3
+	invalid M 7 R 4
+	invalid M 7 R 5
+	invalid M 7 R 6
+	invalid M 7 R 7
+	
+	m68k_fetch_dst_ea M R 1
+	rlc dst 1 dst 1
+	update_flags XNZV0C
+	m68k_save_dst 1
+	m68k_prefetch
--- a/m68k_util.c	Mon Feb 03 22:28:20 2025 -0800
+++ b/m68k_util.c	Fri Feb 07 08:57:24 2025 -0800
@@ -26,11 +26,13 @@
 
 void m68k_sync_cycle(m68k_context *context, uint32_t target_cycle)
 {
-	//TODO: interrupt stuff
-	context->sync_cycle = target_cycle;
+	context->sync_cycle = target_cycle; //why?
+	context->sync_components(context, 0);
 }
 
-void init_m68k_opts(m68k_options *opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider, sync_fun sync_components, int_ack_fun int_ack)
+static sync_fun *sync_comp_tmp;
+static int_ack_fun int_ack_tmp;
+void init_m68k_opts(m68k_options *opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider, sync_fun *sync_components, int_ack_fun int_ack)
 {
 	memset(opts, 0, sizeof(*opts));
 	opts->gen.memmap = memmap;
@@ -40,14 +42,20 @@
 	opts->gen.max_address = 0x1000000;
 	opts->gen.bus_cycles = 4;
 	opts->gen.clock_divider = clock_divider;
+	sync_comp_tmp = sync_components;
+	int_ack_tmp = int_ack;
 }
 
-m68k_context *init_68k_context(m68k_options * opts, m68k_reset_handler reset_handler)
+m68k_context *init_68k_context(m68k_options * opts, m68k_reset_handler *reset_handler)
 {
 	m68k_context *context = calloc(1, sizeof(m68k_context));
 	context->opts = opts;
 	context->reset_handler = reset_handler;
 	context->int_cycle = 0xFFFFFFFFU;
+	context->sync_components = sync_comp_tmp;
+	sync_comp_tmp = NULL;
+	context->int_ack_handler = int_ack_tmp;
+	int_ack_tmp = NULL;
 	return context;
 }