diff --git a/z80.h b/z80.h index 341b690..66c8393 100644 --- a/z80.h +++ b/z80.h @@ -225,6 +225,7 @@ class root { unsigned on_get_int_mode() const { return 0; } void on_set_int_mode(unsigned mode) { unused(mode); } void on_set_is_int_disabled(bool f) { unused(f); } + fast_u8 on_get_int_vector() { return 0xFF; } void set_i_on_ld(fast_u8 i) { self().on_set_i(i); } @@ -3771,7 +3772,7 @@ class internals::executor_base : public B { break; case 2: { // ack(7) w(3) w(3) r(3) r(3) - fast_u16 vector_addr = make16(self().on_get_i(), 0xff); + fast_u16 vector_addr = make16(self().on_get_i(), self().on_get_int_vector()); fast_u8 lo = self().on_read_cycle(vector_addr); fast_u8 hi = self().on_read_cycle(inc16(vector_addr)); isr_addr = make16(hi, lo); } diff --git a/z80/_z80module.cpp b/z80/_z80module.cpp index edcede2..776cc24 100644 --- a/z80/_z80module.cpp +++ b/z80/_z80module.cpp @@ -72,13 +72,58 @@ class machine : public B { } fast_u8 on_read(fast_u16 addr) { + const fast_u8 default_value = 0xff; assert(addr < z80::address_space_size); - return state.memory[addr]; + + if(!on_read_callback) { + return state.memory[addr]; + } + + PyObject *arg = Py_BuildValue("(i)", addr); + decref_guard arg_guard(arg); + + PyObject *result = PyObject_CallObject(on_read_callback, arg); + decref_guard result_guard(result); + + if(!result) { + assert(0); // TODO: stop(); + return default_value; + } + + if(!PyLong_Check(result)) { + PyErr_SetString(PyExc_TypeError, "returning value must be integer"); + assert(0); // TODO: stop(); + return default_value; + } + + return z80::mask8(PyLong_AsUnsignedLong(result)); + } + + PyObject *set_read_callback(PyObject *callback) { + PyObject *old_callback = on_read_callback; + on_read_callback = callback; + return old_callback; } void on_write(fast_u16 addr, fast_u8 n) { assert(addr < z80::address_space_size); - state.memory[addr] = n; + + if(!on_write_callback) { + state.memory[addr] = n; + return; + } + + PyObject *args = Py_BuildValue("(i, i)", addr, n); + decref_guard arg_guard(args); + + PyObject *result = PyObject_CallObject(on_write_callback, args); + decref_guard result_guard(result); + } + + PyObject *set_write_callback(PyObject *callback) { + PyObject *old_callback = on_write_callback; + on_write_callback = callback; + return old_callback; } fast_u8 on_input(fast_u16 addr) { @@ -129,6 +174,49 @@ class machine : public B { return old_callback; } + void on_reti() { + base::on_reti(); + + if(on_reti_callback) { + PyObject *result = PyObject_CallObject(on_reti_callback, NULL); + decref_guard result_guard(result); + } + } + + PyObject *set_reti_callback(PyObject *callback) { + PyObject *old_callback = on_reti_callback; + on_reti_callback = callback; + return old_callback; + } + + fast_u8 on_get_int_vector() { + const fast_u8 default_value = 0xff; + if(!on_get_int_vector_callback) + return base::on_get_int_vector(); + + PyObject *result = PyObject_CallObject(on_get_int_vector_callback, NULL); + decref_guard result_guard(result); + + if(!result) { + assert(0); // TODO: stop(); + return default_value; + } + + if(!PyLong_Check(result)) { + PyErr_SetString(PyExc_TypeError, "returning value must be integer"); + assert(0); // TODO: stop(); + return default_value; + } + + return z80::mask8(PyLong_AsUnsignedLong(result)); + } + + PyObject *set_get_int_vector_callback(PyObject *callback) { + PyObject *old_callback = on_get_int_vector_callback; + on_get_int_vector_callback = callback; + return old_callback; + } + fast_u8 on_get_b() const { return state.b; } void on_set_b(fast_u8 n) { state.b = n; } @@ -224,8 +312,12 @@ class machine : public B { machine_state state; private: + PyObject *on_read_callback = nullptr; + PyObject *on_write_callback = nullptr; PyObject *on_input_callback = nullptr; PyObject *on_output_callback = nullptr; + PyObject *on_reti_callback = nullptr; + PyObject *on_get_int_vector_callback = nullptr; }; static const unsigned max_instr_size = 4; diff --git a/z80/machine.inc b/z80/machine.inc index 00bb9b8..171ac00 100644 --- a/z80/machine.inc +++ b/z80/machine.inc @@ -197,6 +197,40 @@ static PyObject *unmark_addrs(PyObject *self, PyObject *args) { Py_RETURN_NONE; } +static PyObject *set_read_callback(PyObject *self, PyObject *args) { + PyObject *new_callback; + if(!PyArg_ParseTuple(args, "O:set_callback", &new_callback)) + return nullptr; + + if(!PyCallable_Check(new_callback)) { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return nullptr; + } + + auto &machine = cast_machine(self); + PyObject *old_callback = machine.set_read_callback(new_callback); + Py_XINCREF(new_callback); + Py_XDECREF(old_callback); + Py_RETURN_NONE; +} + +static PyObject *set_write_callback(PyObject *self, PyObject *args) { + PyObject *new_callback; + if(!PyArg_ParseTuple(args, "O:set_callback", &new_callback)) + return nullptr; + + if(!PyCallable_Check(new_callback)) { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return nullptr; + } + + auto &machine = cast_machine(self); + PyObject *old_callback = machine.set_write_callback(new_callback); + Py_XINCREF(new_callback); + Py_XDECREF(old_callback); + Py_RETURN_NONE; +} + static PyObject *set_input_callback(PyObject *self, PyObject *args) { PyObject *new_callback; if(!PyArg_ParseTuple(args, "O:set_callback", &new_callback)) @@ -231,6 +265,40 @@ static PyObject *set_output_callback(PyObject *self, PyObject *args) { Py_RETURN_NONE; } +static PyObject *set_reti_callback(PyObject *self, PyObject *args) { + PyObject *new_callback; + if(!PyArg_ParseTuple(args, "O:set_callback", &new_callback)) + return nullptr; + + if(!PyCallable_Check(new_callback)) { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return nullptr; + } + + auto &machine = cast_machine(self); + PyObject *old_callback = machine.set_reti_callback(new_callback); + Py_XINCREF(new_callback); + Py_XDECREF(old_callback); + Py_RETURN_NONE; +} + +static PyObject *set_get_int_vector_callback(PyObject *self, PyObject *args) { + PyObject *new_callback; + if(!PyArg_ParseTuple(args, "O:set_callback", &new_callback)) + return nullptr; + + if(!PyCallable_Check(new_callback)) { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return nullptr; + } + + auto &machine = cast_machine(self); + PyObject *old_callback = machine.set_get_int_vector_callback(new_callback); + Py_XINCREF(new_callback); + Py_XDECREF(old_callback); + Py_RETURN_NONE; +} + static PyObject *run(PyObject *self, PyObject *args) { auto &machine = cast_machine(self); z80::events_mask::type events = machine.on_run(); @@ -281,10 +349,18 @@ static PyMethodDef methods[] = { {"unmark_addrs", unmark_addrs, METH_VARARGS, "Clear the marking on a range of memory bytes that indicates it requires custom " "processing on reading, writing or executing them."}, + {"set_read_callback", set_read_callback, METH_VARARGS, + "Set a callback function handling reading from memory."}, + {"set_write_callback", set_write_callback, METH_VARARGS, + "Set a callback function handling writing to memory."}, {"set_input_callback", set_input_callback, METH_VARARGS, "Set a callback function handling reading from ports."}, {"set_output_callback", set_output_callback, METH_VARARGS, "Set a callback function handling writing to ports."}, + {"set_reti_callback", set_reti_callback, METH_VARARGS, + "Set a callback function handling the reti instruction."}, + {"set_get_int_vector_callback", set_get_int_vector_callback, METH_VARARGS, + "Set a callback function handling the interrupt mode 2 vector address."}, {"run", run, METH_NOARGS, "Run emulator until one or several events are signaled."}, #if defined(Z80_MACHINE)