z80 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +15 -0
- data/.github/FUNDING.yml +4 -0
- data/.gitignore +17 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE-0BSD +14 -0
- data/README.md +164 -0
- data/Rakefile +5 -0
- data/ext/z80/extconf.rb +97 -0
- data/ext/z80/z80.c +791 -0
- data/lib/z80/version.rb +3 -0
- data/lib/z80.rb +2 -0
- metadata +62 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a5289868d57c23014c7ebf04e4a895cfb66743a55bd158f33802f99b8da91ca8
|
4
|
+
data.tar.gz: 3d33827c3ea92ef3e6194a9b1f60eb6311763aa421ec0e6e459766f6242ee04b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: efffd69b66de491f606dec0271479dad51bd1d8a698582e65c3f566da4448bd0e7c5e5ff8bd04c3fa3ef0f1d8bb9d9434abc820c9b71fc916d5eb842d97834d4
|
7
|
+
data.tar.gz: 6909aa3c155aa3f957ee71e160efdef14984232042f9f1f918e171598a5bf24958d15a62d4f482c7d44317d850aac58abc153042aa70f7c968439c85b334706b
|
data/.editorconfig
ADDED
data/.github/FUNDING.yml
ADDED
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/LICENSE-0BSD
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
BSD Zero Clause License
|
2
|
+
|
3
|
+
Copyright (C) 2023 Manuel Sainz de Baranda y Goñi.
|
4
|
+
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
6
|
+
purpose with or without fee is hereby granted.
|
7
|
+
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
9
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
10
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
11
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
12
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
13
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
14
|
+
PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# Z80-Ruby
|
2
|
+
|
3
|
+
Ruby binding for the [Z80](https://github.com/redcode/Z80) library.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
First make sure that you have the Z80 library [installed](https://github.com/redcode/Z80#installation) on your system.
|
8
|
+
|
9
|
+
Then add `z80` to the `Gemfile` of your project and run `bundle`:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'z80'
|
13
|
+
```
|
14
|
+
|
15
|
+
Or install the gem directly with `gem`:
|
16
|
+
|
17
|
+
```shell
|
18
|
+
gem install z80
|
19
|
+
```
|
20
|
+
|
21
|
+
## Examples
|
22
|
+
|
23
|
+
This small script demonstrates how to run the [CP/M version of ZEXALL](https://github.com/redcode/Z80/wiki/Z80-Instruction-Set-Exerciser) with a few lines of code:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'z80'
|
27
|
+
|
28
|
+
quit = false
|
29
|
+
memory = Array.new(65536, 0)
|
30
|
+
cpu = Z80.new
|
31
|
+
|
32
|
+
cpu.fetch_opcode = cpu.fetch = cpu.read do |context, address|
|
33
|
+
memory[address]
|
34
|
+
end
|
35
|
+
|
36
|
+
cpu.write do |context, address, value|
|
37
|
+
memory[address] = value
|
38
|
+
end
|
39
|
+
|
40
|
+
cpu.hook do |context, address|
|
41
|
+
case address
|
42
|
+
when 0 # END
|
43
|
+
cpu.terminate
|
44
|
+
quit = true
|
45
|
+
0 # NOP
|
46
|
+
when 5 # PRINT
|
47
|
+
i = cpu.de
|
48
|
+
while (c = memory[i]) != 0x24
|
49
|
+
putc(c == 0xA ? "\n" : c) if c != 0xD
|
50
|
+
i = (i + 1) & 0xFFFF
|
51
|
+
end
|
52
|
+
0xC9 # RET
|
53
|
+
else
|
54
|
+
0 # NOP
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
program = File.read(ARGV[0])
|
59
|
+
memory[0x0100, program.size] = program.bytes
|
60
|
+
memory[0] = memory[5] = Z80::HOOK
|
61
|
+
cpu.power true
|
62
|
+
cpu.pc = 0x0100
|
63
|
+
cpu.run(Z80::MAXIMUM_CYCLES) until quit
|
64
|
+
puts
|
65
|
+
```
|
66
|
+
|
67
|
+
<sup>**[<sub><img src="https://zxe.io/software/Z80/assets/images/ruby-icon.svg" height="14"></sub> run-yaze-zexall.rb](https://zxe.io/software/Z80/scripts/run-yaze-zexall.rb)**</sup>
|
68
|
+
|
69
|
+
This runs any tape from Patrik Rak's [Zilog Z80 CPU Test Suite](https://github.com/raxoft/z80test) (except `z80ccfscr.tap`):
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
require 'z80'
|
73
|
+
|
74
|
+
module Opcode
|
75
|
+
RET = 0xC9
|
76
|
+
NOP = 0x00
|
77
|
+
CALL = 0xCD
|
78
|
+
end
|
79
|
+
|
80
|
+
quit = false
|
81
|
+
tab = 0
|
82
|
+
cursor_x = 0
|
83
|
+
memory = Array.new(65536, 0)
|
84
|
+
cpu = Z80.new
|
85
|
+
|
86
|
+
cpu.fetch_opcode = cpu.fetch = cpu.read do |context, address|
|
87
|
+
memory[address]
|
88
|
+
end
|
89
|
+
|
90
|
+
cpu.write do |context, address, value|
|
91
|
+
memory[address] = value if address > 0x3FFF
|
92
|
+
end
|
93
|
+
|
94
|
+
cpu.in do |context, port|
|
95
|
+
port.odd? ? 255 : 191
|
96
|
+
end
|
97
|
+
|
98
|
+
cpu.hook do |context, address|
|
99
|
+
case address
|
100
|
+
when 0x0010 # PRINT
|
101
|
+
if tab == 0
|
102
|
+
case (c = cpu.a)
|
103
|
+
when 0x0D # CR
|
104
|
+
putc "\n"
|
105
|
+
cursor_x = 0
|
106
|
+
when 0x17 # TAB
|
107
|
+
tab = 2
|
108
|
+
when 0x7F # ©
|
109
|
+
printf "©"
|
110
|
+
cursor_x += 1
|
111
|
+
else
|
112
|
+
if c >= 32 && c < 127
|
113
|
+
putc c
|
114
|
+
cursor_x += 1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
elsif (tab -= 1) != 0
|
118
|
+
c = 0x1F & cpu.a
|
119
|
+
x = 0x1F & cursor_x
|
120
|
+
if c < x
|
121
|
+
putc "\n"
|
122
|
+
cursor_x = 0
|
123
|
+
else
|
124
|
+
cursor_x += (c -= x)
|
125
|
+
end
|
126
|
+
print ' ' * c
|
127
|
+
end
|
128
|
+
Opcode::RET
|
129
|
+
when 0x7003 # Exit
|
130
|
+
cpu.terminate
|
131
|
+
quit = true
|
132
|
+
Opcode::NOP
|
133
|
+
else
|
134
|
+
Opcode::NOP
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
program = File.read(ARGV[0])
|
139
|
+
memory[0x8000, program.size - 91] = program.bytes[91..-1]
|
140
|
+
memory[0x0010] = Z80::HOOK # THE 'PRINT A CHARACTER' RESTART
|
141
|
+
memory[0x0D6B] = Opcode::RET # THE 'CLS' COMMAND ROUTINE
|
142
|
+
memory[0x1601] = Opcode::RET # THE 'CHAN_OPEN' SUBROUTINE
|
143
|
+
memory[0x7000] = Opcode::CALL # -.
|
144
|
+
memory[0x7001] = 0x00 # |- call 8000h
|
145
|
+
memory[0x7002] = 0x80 # -'
|
146
|
+
memory[0x7003] = Z80::HOOK
|
147
|
+
cpu.power true
|
148
|
+
cpu.im = 1
|
149
|
+
cpu.i = 0x3F
|
150
|
+
cpu.pc = 0x7000
|
151
|
+
cpu.run(Z80::MAXIMUM_CYCLES) until quit
|
152
|
+
```
|
153
|
+
|
154
|
+
<sup>**[<sub><img src="https://zxe.io/software/Z80/assets/images/ruby-icon.svg" height="14"></sub> run-raxoft-z80test.rb](https://zxe.io/software/Z80/scripts/run-raxoft-z80test.rb)**</sup>
|
155
|
+
|
156
|
+
## License
|
157
|
+
|
158
|
+
<img src="https://zxe.io/software/Z80/assets/images/0bsd.svg" width="160" align="right">
|
159
|
+
|
160
|
+
Copyright © 2023 Manuel Sainz de Baranda y Goñi.
|
161
|
+
|
162
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
163
|
+
|
164
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/Rakefile
ADDED
data/ext/z80/extconf.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
dir_config 'Z80'
|
3
|
+
|
4
|
+
abort "missing header Z80.h" unless have_header 'Z80.h'
|
5
|
+
abort "missing library Z80" unless have_library 'Z80'
|
6
|
+
|
7
|
+
%w(
|
8
|
+
z80_power
|
9
|
+
z80_instant_reset
|
10
|
+
z80_int
|
11
|
+
z80_nmi
|
12
|
+
z80_execute
|
13
|
+
z80_run
|
14
|
+
).each do |function|
|
15
|
+
abort "missing #{function}()" unless have_func function
|
16
|
+
end
|
17
|
+
|
18
|
+
have_func 'z80_special_reset'
|
19
|
+
|
20
|
+
%w(
|
21
|
+
z80_break
|
22
|
+
z80_r
|
23
|
+
z80_refresh_address
|
24
|
+
z80_in_cycle
|
25
|
+
z80_out_cycle
|
26
|
+
).each do |function|
|
27
|
+
abort "missing #{function}()" unless have_func(function, 'Z80.h')
|
28
|
+
end
|
29
|
+
|
30
|
+
%w(
|
31
|
+
Z80_MAXIMUM_CYCLES
|
32
|
+
Z80_MAXIMUM_CYCLES_PER_STEP
|
33
|
+
Z80_MINIMUM_CYCLES_PER_STEP
|
34
|
+
Z80_HOOK
|
35
|
+
Z80_OPTION_OUT_VC_255
|
36
|
+
Z80_OPTION_LD_A_IR_BUG
|
37
|
+
Z80_OPTION_HALT_SKIP
|
38
|
+
Z80_OPTION_XQ
|
39
|
+
Z80_OPTION_IM0_RETX_NOTIFICATIONS
|
40
|
+
Z80_OPTION_YQ
|
41
|
+
Z80_MODEL_ZILOG_NMOS
|
42
|
+
Z80_MODEL_ZILOG_CMOS
|
43
|
+
Z80_MODEL_NEC_NMOS
|
44
|
+
Z80_MODEL_ST_CMOS
|
45
|
+
Z80_REQUEST_REJECT_NMI
|
46
|
+
Z80_REQUEST_NMI
|
47
|
+
Z80_REQUEST_INT
|
48
|
+
Z80_RESUME_HALT
|
49
|
+
Z80_RESUME_XY
|
50
|
+
Z80_RESUME_IM0_XY
|
51
|
+
Z80_MEMPTR
|
52
|
+
Z80_PC
|
53
|
+
Z80_SP
|
54
|
+
Z80_XY
|
55
|
+
Z80_IX
|
56
|
+
Z80_IY
|
57
|
+
Z80_AF
|
58
|
+
Z80_BC
|
59
|
+
Z80_DE
|
60
|
+
Z80_HL
|
61
|
+
Z80_AF_
|
62
|
+
Z80_BC_
|
63
|
+
Z80_DE_
|
64
|
+
Z80_HL_
|
65
|
+
Z80_MEMPTRH
|
66
|
+
Z80_MEMPTRL
|
67
|
+
Z80_PCH
|
68
|
+
Z80_PCL
|
69
|
+
Z80_SPH
|
70
|
+
Z80_SPL
|
71
|
+
Z80_XYH
|
72
|
+
Z80_XYL
|
73
|
+
Z80_IXH
|
74
|
+
Z80_IXL
|
75
|
+
Z80_IYH
|
76
|
+
Z80_IYL
|
77
|
+
Z80_A
|
78
|
+
Z80_F
|
79
|
+
Z80_B
|
80
|
+
Z80_C
|
81
|
+
Z80_D
|
82
|
+
Z80_E
|
83
|
+
Z80_H
|
84
|
+
Z80_L
|
85
|
+
Z80_A_
|
86
|
+
Z80_F_
|
87
|
+
Z80_B_
|
88
|
+
Z80_C_
|
89
|
+
Z80_D_
|
90
|
+
Z80_E_
|
91
|
+
Z80_H_
|
92
|
+
Z80_L_
|
93
|
+
).each do |macro|
|
94
|
+
abort "missing #{macro}" unless have_macro(macro, 'Z80.h')
|
95
|
+
end
|
96
|
+
|
97
|
+
create_makefile "z80/z80"
|
data/ext/z80/z80.c
ADDED
@@ -0,0 +1,791 @@
|
|
1
|
+
/* ______ ______ ______
|
2
|
+
/\___ \/\ __ \\ __ \
|
3
|
+
____ \/__/ /\_\ __ \\ \/\ \ ______________________________________
|
4
|
+
| /\_____\\_____\\_____\ |
|
5
|
+
| Zilog \/_____//_____//_____/ CPU Emulator - Ruby Binding |
|
6
|
+
| Copyright (C) 2023 Manuel Sainz de Baranda y Goñi. |
|
7
|
+
| |
|
8
|
+
| Permission to use, copy, modify, and/or distribute this software |
|
9
|
+
| for any purpose with or without fee is hereby granted. |
|
10
|
+
| |
|
11
|
+
| THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
|
12
|
+
| WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
|
13
|
+
| WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL |
|
14
|
+
| THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR |
|
15
|
+
| CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM |
|
16
|
+
| LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, |
|
17
|
+
| NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|
18
|
+
| CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
19
|
+
| |
|
20
|
+
'===================================================================*/
|
21
|
+
|
22
|
+
#include <ruby.h>
|
23
|
+
#include <Z80.h>
|
24
|
+
#include <Z/macros/array.h>
|
25
|
+
#include <inttypes.h>
|
26
|
+
#include <stdio.h>
|
27
|
+
|
28
|
+
static rb_data_type_t const z80_data_type;
|
29
|
+
|
30
|
+
#define GET_Z80 \
|
31
|
+
Z80 *z80; \
|
32
|
+
TypedData_Get_Struct(self, Z80, &z80_data_type, z80);
|
33
|
+
|
34
|
+
enum { FetchOpcode, Fetch, Read, Write, In, Out,
|
35
|
+
Halt, Nop,
|
36
|
+
NMIA, INTA, INTFetch,
|
37
|
+
ld_i_a, ld_r_a, reti, retn,
|
38
|
+
Hook, Illegal,
|
39
|
+
Context
|
40
|
+
};
|
41
|
+
|
42
|
+
|
43
|
+
/* Callbacks: Dummy Bridges */
|
44
|
+
|
45
|
+
static zuint8 dummy_read(void *context, zuint16 address)
|
46
|
+
{
|
47
|
+
Z_UNUSED(context) Z_UNUSED(address)
|
48
|
+
return 0; /* nop */
|
49
|
+
}
|
50
|
+
|
51
|
+
|
52
|
+
static void dummy_write(void *context, zuint16 address, zuint8 value)
|
53
|
+
{Z_UNUSED(context) Z_UNUSED(address)}
|
54
|
+
|
55
|
+
|
56
|
+
static zuint8 dummy_in(void *context, zuint16 port)
|
57
|
+
{
|
58
|
+
Z_UNUSED(context) Z_UNUSED(port)
|
59
|
+
return 255;
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
/* Callbacks: Bridges */
|
64
|
+
|
65
|
+
#define PROC_CALL(index, arity, ...) \
|
66
|
+
rb_funcall( \
|
67
|
+
external[index], rb_intern("call"), arity + 1, \
|
68
|
+
external[Context] Z_IF(arity)(Z_COMMA) __VA_ARGS__)
|
69
|
+
|
70
|
+
#define METHOD_CALL(index, arity, ...) \
|
71
|
+
rb_funcall( \
|
72
|
+
external[index], rb_intern("call"), arity \
|
73
|
+
Z_IF(arity)(Z_COMMA) __VA_ARGS__)
|
74
|
+
|
75
|
+
|
76
|
+
#define CALLBACK_BRIDGES(receiver, call) \
|
77
|
+
\
|
78
|
+
static zuint8 receiver##_fetch_opcode(VALUE *external, zuint16 address) \
|
79
|
+
{return (zuint8)NUM2UINT(call(FetchOpcode, 1, UINT2NUM(address)));} \
|
80
|
+
\
|
81
|
+
\
|
82
|
+
static zuint8 receiver##_fetch(VALUE *external, zuint16 address) \
|
83
|
+
{return (zuint8)NUM2UINT(call(Fetch, 1, UINT2NUM(address)));} \
|
84
|
+
\
|
85
|
+
\
|
86
|
+
static zuint8 receiver##_read(VALUE *external, zuint16 address) \
|
87
|
+
{return (zuint8)NUM2UINT(call(Read, 1, UINT2NUM(address)));} \
|
88
|
+
\
|
89
|
+
\
|
90
|
+
static void receiver##_write(VALUE *external, zuint16 address, zuint8 value) \
|
91
|
+
{call(Write, 2, UINT2NUM(address), UINT2NUM(value));} \
|
92
|
+
\
|
93
|
+
\
|
94
|
+
static zuint8 receiver##_in(VALUE *external, zuint16 port) \
|
95
|
+
{return (zuint8)NUM2UINT(call(In, 1, UINT2NUM(port)));} \
|
96
|
+
\
|
97
|
+
\
|
98
|
+
static void receiver##_out(VALUE *external, zuint16 port, zuint8 value) \
|
99
|
+
{call(Out, 2, UINT2NUM(port), UINT2NUM(value));} \
|
100
|
+
\
|
101
|
+
\
|
102
|
+
static void receiver##_halt(VALUE *external, zuint8 state) \
|
103
|
+
{call(Halt, 1, UINT2NUM(state));} \
|
104
|
+
\
|
105
|
+
\
|
106
|
+
static zuint8 receiver##_nop(VALUE *external, zuint16 address) \
|
107
|
+
{return (zuint8)NUM2UINT(call(Nop, 1, UINT2NUM(address)));} \
|
108
|
+
\
|
109
|
+
\
|
110
|
+
static zuint8 receiver##_nmia(VALUE *external, zuint16 address) \
|
111
|
+
{return (zuint8)NUM2UINT(call(NMIA, 1, UINT2NUM(address)));} \
|
112
|
+
\
|
113
|
+
\
|
114
|
+
static zuint8 receiver##_inta(VALUE *external, zuint16 address) \
|
115
|
+
{return (zuint8)NUM2UINT(call(INTA, 1, UINT2NUM(address)));} \
|
116
|
+
\
|
117
|
+
\
|
118
|
+
static zuint8 receiver##_int_fetch(VALUE *external, zuint16 address) \
|
119
|
+
{return (zuint8)NUM2UINT(call(INTFetch, 1, UINT2NUM(address)));} \
|
120
|
+
\
|
121
|
+
\
|
122
|
+
static void receiver##_ld_i_a(VALUE *external) {call(ld_i_a, 0);} \
|
123
|
+
static void receiver##_ld_r_a(VALUE *external) {call(ld_r_a, 0);} \
|
124
|
+
static void receiver##_reti (VALUE *external) {call(reti, 0);} \
|
125
|
+
static void receiver##_retn (VALUE *external) {call(reti, 0);} \
|
126
|
+
\
|
127
|
+
\
|
128
|
+
static zuint8 receiver##_hook(VALUE *external, zuint16 address) \
|
129
|
+
{return (zuint8)NUM2UINT(call(Hook, 1, UINT2NUM(address)));} \
|
130
|
+
\
|
131
|
+
\
|
132
|
+
static zuint8 receiver##_illegal(VALUE *external, zuint8 opcode) \
|
133
|
+
{return (zuint8)NUM2UINT(call(Illegal, 1, UINT2NUM(opcode)));}
|
134
|
+
|
135
|
+
|
136
|
+
CALLBACK_BRIDGES(proc, PROC_CALL )
|
137
|
+
CALLBACK_BRIDGES(method, METHOD_CALL)
|
138
|
+
|
139
|
+
#undef PROC_CALL
|
140
|
+
#undef METHOD_CALL
|
141
|
+
#undef CALLBACK_BRIDGES
|
142
|
+
|
143
|
+
|
144
|
+
/* MARK: - Callbacks: Accessors */
|
145
|
+
|
146
|
+
typedef struct {
|
147
|
+
zusize offset;
|
148
|
+
void* dummy;
|
149
|
+
void* proc_bridge;
|
150
|
+
void* method_bridge;
|
151
|
+
} CallbackInfo;
|
152
|
+
|
153
|
+
static CallbackInfo const callback_info_table[] = {
|
154
|
+
{Z_MEMBER_OFFSET(Z80, fetch_opcode), dummy_read, proc_fetch_opcode, method_fetch_opcode},
|
155
|
+
{Z_MEMBER_OFFSET(Z80, fetch ), dummy_read, proc_fetch, method_fetch, },
|
156
|
+
{Z_MEMBER_OFFSET(Z80, read ), dummy_read, proc_read, method_read, },
|
157
|
+
{Z_MEMBER_OFFSET(Z80, write ), dummy_write, proc_write, method_write, },
|
158
|
+
{Z_MEMBER_OFFSET(Z80, in ), dummy_read, proc_in, method_in, },
|
159
|
+
{Z_MEMBER_OFFSET(Z80, out ), dummy_write, proc_out, method_out, },
|
160
|
+
{Z_MEMBER_OFFSET(Z80, halt ), NULL, proc_halt, method_halt, },
|
161
|
+
{Z_MEMBER_OFFSET(Z80, nop ), NULL, proc_nop, method_nop, },
|
162
|
+
{Z_MEMBER_OFFSET(Z80, nmia ), NULL, proc_nmia, method_nmia, },
|
163
|
+
{Z_MEMBER_OFFSET(Z80, inta ), NULL, proc_inta, method_inta, },
|
164
|
+
{Z_MEMBER_OFFSET(Z80, int_fetch ), NULL, proc_int_fetch, method_int_fetch, },
|
165
|
+
{Z_MEMBER_OFFSET(Z80, ld_i_a ), NULL, proc_ld_i_a, method_ld_i_a, },
|
166
|
+
{Z_MEMBER_OFFSET(Z80, ld_r_a ), NULL, proc_ld_r_a, method_ld_r_a, },
|
167
|
+
{Z_MEMBER_OFFSET(Z80, reti ), NULL, proc_reti, method_reti, },
|
168
|
+
{Z_MEMBER_OFFSET(Z80, retn ), NULL, proc_retn, method_retn, },
|
169
|
+
{Z_MEMBER_OFFSET(Z80, hook ), NULL, proc_hook, method_hook, },
|
170
|
+
{Z_MEMBER_OFFSET(Z80, illegal ), NULL, proc_illegal, method_illegal, }
|
171
|
+
};
|
172
|
+
|
173
|
+
|
174
|
+
static void set_callback(VALUE self, VALUE object, zuint index)
|
175
|
+
{
|
176
|
+
CallbackInfo const *callback_info;
|
177
|
+
VALUE *external;
|
178
|
+
GET_Z80;
|
179
|
+
|
180
|
+
external = (VALUE *)z80->context + index;
|
181
|
+
*external = object;
|
182
|
+
callback_info = callback_info_table + index;
|
183
|
+
|
184
|
+
*(void **)((char *)z80 + callback_info->offset) = object != Qnil
|
185
|
+
? *(void **)((char *)z80 + callback_info->offset) = callback_info->proc_bridge
|
186
|
+
: callback_info->dummy;
|
187
|
+
}
|
188
|
+
|
189
|
+
|
190
|
+
#define CALLBACK_ACCESSOR(member, index) \
|
191
|
+
\
|
192
|
+
static VALUE Z80__set_##member(VALUE self, VALUE callback) \
|
193
|
+
{ \
|
194
|
+
set_callback(self, callback, index); \
|
195
|
+
return Qnil; \
|
196
|
+
} \
|
197
|
+
\
|
198
|
+
\
|
199
|
+
static VALUE Z80__##member(VALUE self) \
|
200
|
+
{ \
|
201
|
+
GET_Z80; \
|
202
|
+
if (rb_block_given_p()) set_callback(self, rb_block_proc(), index); \
|
203
|
+
return *((VALUE *)z80->context + index); \
|
204
|
+
}
|
205
|
+
|
206
|
+
|
207
|
+
CALLBACK_ACCESSOR(fetch_opcode, FetchOpcode)
|
208
|
+
CALLBACK_ACCESSOR(fetch, Fetch )
|
209
|
+
CALLBACK_ACCESSOR(read, Read )
|
210
|
+
CALLBACK_ACCESSOR(write, Write )
|
211
|
+
CALLBACK_ACCESSOR(in, In )
|
212
|
+
CALLBACK_ACCESSOR(out, Out )
|
213
|
+
CALLBACK_ACCESSOR(halt, Halt )
|
214
|
+
CALLBACK_ACCESSOR(nop, Nop )
|
215
|
+
CALLBACK_ACCESSOR(nmia, NMIA )
|
216
|
+
CALLBACK_ACCESSOR(inta, INTA )
|
217
|
+
CALLBACK_ACCESSOR(int_fetch, INTFetch )
|
218
|
+
CALLBACK_ACCESSOR(ld_i_a, ld_i_a )
|
219
|
+
CALLBACK_ACCESSOR(ld_r_a, ld_r_a )
|
220
|
+
CALLBACK_ACCESSOR(reti, reti )
|
221
|
+
CALLBACK_ACCESSOR(retn, retn )
|
222
|
+
CALLBACK_ACCESSOR(hook, Hook )
|
223
|
+
CALLBACK_ACCESSOR(illegal, Illegal )
|
224
|
+
|
225
|
+
#undef CALLBACK_ACCESSOR
|
226
|
+
|
227
|
+
|
228
|
+
/* MARK: - Other Accessors */
|
229
|
+
|
230
|
+
static VALUE Z80__set_context(VALUE self, VALUE context)
|
231
|
+
{
|
232
|
+
GET_Z80;
|
233
|
+
*((VALUE *)z80->context + Context) = context;
|
234
|
+
return Qnil;
|
235
|
+
}
|
236
|
+
|
237
|
+
|
238
|
+
static VALUE Z80__context(VALUE self)
|
239
|
+
{
|
240
|
+
GET_Z80;
|
241
|
+
return *((VALUE *)z80->context + Context);
|
242
|
+
}
|
243
|
+
|
244
|
+
|
245
|
+
#define MACRO( member, macro) macro(*z80)
|
246
|
+
#define DIRECT(member, macro) z80->member
|
247
|
+
#define UINT_TO_BOOL(value) value ? Qtrue : Qfalse
|
248
|
+
|
249
|
+
#define BOOL_NUM_TO_UINT(value) \
|
250
|
+
(value == Qfalse ? 0 : (value == Qtrue ? 1 : !!NUM2UINT(value)))
|
251
|
+
|
252
|
+
|
253
|
+
#define ACCESSOR(type, member, access, with, c_to_ruby, ruby_to_c) \
|
254
|
+
\
|
255
|
+
static VALUE Z80__##member(VALUE self) \
|
256
|
+
{ \
|
257
|
+
GET_Z80; \
|
258
|
+
return c_to_ruby(access(member, with)); \
|
259
|
+
} \
|
260
|
+
\
|
261
|
+
\
|
262
|
+
static VALUE Z80__set_##member(VALUE self, VALUE value) \
|
263
|
+
{ \
|
264
|
+
GET_Z80; \
|
265
|
+
access(member, with) = (type)ruby_to_c(value); \
|
266
|
+
return value; \
|
267
|
+
}
|
268
|
+
|
269
|
+
|
270
|
+
ACCESSOR(zusize, cycles, DIRECT, Z_EMPTY, SIZET2NUM, NUM2SIZET )
|
271
|
+
ACCESSOR(zusize, cycle_limit, DIRECT, Z_EMPTY, SIZET2NUM, NUM2SIZET )
|
272
|
+
ACCESSOR(zuint16, memptr, MACRO, Z80_MEMPTR, UINT2NUM, NUM2UINT )
|
273
|
+
ACCESSOR(zuint16, pc, MACRO, Z80_PC, UINT2NUM, NUM2UINT )
|
274
|
+
ACCESSOR(zuint16, sp, MACRO, Z80_SP, UINT2NUM, NUM2UINT )
|
275
|
+
ACCESSOR(zuint16, xy, MACRO, Z80_XY, UINT2NUM, NUM2UINT )
|
276
|
+
ACCESSOR(zuint16, ix, MACRO, Z80_IX, UINT2NUM, NUM2UINT )
|
277
|
+
ACCESSOR(zuint16, iy, MACRO, Z80_IY, UINT2NUM, NUM2UINT )
|
278
|
+
ACCESSOR(zuint16, af, MACRO, Z80_AF, UINT2NUM, NUM2UINT )
|
279
|
+
ACCESSOR(zuint16, bc, MACRO, Z80_BC, UINT2NUM, NUM2UINT )
|
280
|
+
ACCESSOR(zuint16, de, MACRO, Z80_DE, UINT2NUM, NUM2UINT )
|
281
|
+
ACCESSOR(zuint16, hl, MACRO, Z80_HL, UINT2NUM, NUM2UINT )
|
282
|
+
ACCESSOR(zuint16, af_, MACRO, Z80_AF_, UINT2NUM, NUM2UINT )
|
283
|
+
ACCESSOR(zuint16, bc_, MACRO, Z80_BC_, UINT2NUM, NUM2UINT )
|
284
|
+
ACCESSOR(zuint16, de_, MACRO, Z80_DE_, UINT2NUM, NUM2UINT )
|
285
|
+
ACCESSOR(zuint16, hl_, MACRO, Z80_HL_, UINT2NUM, NUM2UINT )
|
286
|
+
ACCESSOR(zuint8, memptrh, MACRO, Z80_MEMPTRH, UINT2NUM, NUM2UINT )
|
287
|
+
ACCESSOR(zuint8, memptrl, MACRO, Z80_MEMPTRL, UINT2NUM, NUM2UINT )
|
288
|
+
ACCESSOR(zuint8, pch, MACRO, Z80_PCH, UINT2NUM, NUM2UINT )
|
289
|
+
ACCESSOR(zuint8, pcl, MACRO, Z80_PCL, UINT2NUM, NUM2UINT )
|
290
|
+
ACCESSOR(zuint8, sph, MACRO, Z80_SPH, UINT2NUM, NUM2UINT )
|
291
|
+
ACCESSOR(zuint8, spl, MACRO, Z80_SPL, UINT2NUM, NUM2UINT )
|
292
|
+
ACCESSOR(zuint8, xyh, MACRO, Z80_XYH, UINT2NUM, NUM2UINT )
|
293
|
+
ACCESSOR(zuint8, yl, MACRO, Z80_XYL, UINT2NUM, NUM2UINT )
|
294
|
+
ACCESSOR(zuint8, ixh, MACRO, Z80_IXH, UINT2NUM, NUM2UINT )
|
295
|
+
ACCESSOR(zuint8, ixl, MACRO, Z80_IXL, UINT2NUM, NUM2UINT )
|
296
|
+
ACCESSOR(zuint8, iyh, MACRO, Z80_IYH, UINT2NUM, NUM2UINT )
|
297
|
+
ACCESSOR(zuint8, iyl, MACRO, Z80_IYL, UINT2NUM, NUM2UINT )
|
298
|
+
ACCESSOR(zuint8, a, MACRO, Z80_A, UINT2NUM, NUM2UINT )
|
299
|
+
ACCESSOR(zuint8, f, MACRO, Z80_F, UINT2NUM, NUM2UINT )
|
300
|
+
ACCESSOR(zuint8, b, MACRO, Z80_B, UINT2NUM, NUM2UINT )
|
301
|
+
ACCESSOR(zuint8, c, MACRO, Z80_C, UINT2NUM, NUM2UINT )
|
302
|
+
ACCESSOR(zuint8, d, MACRO, Z80_D, UINT2NUM, NUM2UINT )
|
303
|
+
ACCESSOR(zuint8, e, MACRO, Z80_E, UINT2NUM, NUM2UINT )
|
304
|
+
ACCESSOR(zuint8, h, MACRO, Z80_H, UINT2NUM, NUM2UINT )
|
305
|
+
ACCESSOR(zuint8, l, MACRO, Z80_L, UINT2NUM, NUM2UINT )
|
306
|
+
ACCESSOR(zuint8, a_, MACRO, Z80_A_, UINT2NUM, NUM2UINT )
|
307
|
+
ACCESSOR(zuint8, f_, MACRO, Z80_F_, UINT2NUM, NUM2UINT )
|
308
|
+
ACCESSOR(zuint8, b_, MACRO, Z80_B_, UINT2NUM, NUM2UINT )
|
309
|
+
ACCESSOR(zuint8, c_, MACRO, Z80_C_, UINT2NUM, NUM2UINT )
|
310
|
+
ACCESSOR(zuint8, d_, MACRO, Z80_D_, UINT2NUM, NUM2UINT )
|
311
|
+
ACCESSOR(zuint8, e_, MACRO, Z80_E_, UINT2NUM, NUM2UINT )
|
312
|
+
ACCESSOR(zuint8, h_, MACRO, Z80_H_, UINT2NUM, NUM2UINT )
|
313
|
+
ACCESSOR(zuint8, l_, MACRO, Z80_L_, UINT2NUM, NUM2UINT )
|
314
|
+
ACCESSOR(zuint8, r, DIRECT, Z_EMPTY, UINT2NUM, NUM2UINT )
|
315
|
+
ACCESSOR(zuint8, i, DIRECT, Z_EMPTY, UINT2NUM, NUM2UINT )
|
316
|
+
ACCESSOR(zuint8, r7, DIRECT, Z_EMPTY, UINT2NUM, NUM2UINT )
|
317
|
+
ACCESSOR(zuint8, im, DIRECT, Z_EMPTY, UINT2NUM, NUM2UINT )
|
318
|
+
ACCESSOR(zuint8, request, DIRECT, Z_EMPTY, UINT2NUM, NUM2UINT )
|
319
|
+
ACCESSOR(zuint8, resume, DIRECT, Z_EMPTY, UINT2NUM, NUM2UINT )
|
320
|
+
ACCESSOR(zuint8, iff1, DIRECT, Z_EMPTY, UINT_TO_BOOL, BOOL_NUM_TO_UINT)
|
321
|
+
ACCESSOR(zuint8, iff2, DIRECT, Z_EMPTY, UINT_TO_BOOL, BOOL_NUM_TO_UINT)
|
322
|
+
ACCESSOR(zuint8, q, DIRECT, Z_EMPTY, UINT2NUM, NUM2UINT )
|
323
|
+
ACCESSOR(zuint8, options, DIRECT, Z_EMPTY, UINT2NUM, NUM2UINT )
|
324
|
+
ACCESSOR(zuint8, int_line, DIRECT, Z_EMPTY, UINT_TO_BOOL, BOOL_NUM_TO_UINT)
|
325
|
+
ACCESSOR(zuint8, halt_line, DIRECT, Z_EMPTY, UINT_TO_BOOL, BOOL_NUM_TO_UINT)
|
326
|
+
|
327
|
+
#undef UINT_TO_BOOL
|
328
|
+
#undef MACRO
|
329
|
+
#undef DIRECT
|
330
|
+
#undef ACCESSOR
|
331
|
+
|
332
|
+
|
333
|
+
#define FLAG_ACCESSOR(flag, shift) \
|
334
|
+
\
|
335
|
+
static VALUE Z80__##flag(VALUE self) \
|
336
|
+
{ \
|
337
|
+
GET_Z80; \
|
338
|
+
return UINT2NUM((Z80_F(*z80) >> shift) & 1); \
|
339
|
+
} \
|
340
|
+
\
|
341
|
+
static VALUE Z80__set_##flag(VALUE self, VALUE value) \
|
342
|
+
{ \
|
343
|
+
zuint8 bit = (zuint8)(NUM2UINT(value) & 1); \
|
344
|
+
GET_Z80; \
|
345
|
+
Z80_F(*z80) = (Z80_F(*z80) & ~(1U << shift)) | (bit << shift); \
|
346
|
+
return UINT2NUM(bit); \
|
347
|
+
}
|
348
|
+
|
349
|
+
|
350
|
+
FLAG_ACCESSOR(sf, 7)
|
351
|
+
FLAG_ACCESSOR(zf, 6)
|
352
|
+
FLAG_ACCESSOR(yf, 5)
|
353
|
+
FLAG_ACCESSOR(hf, 4)
|
354
|
+
FLAG_ACCESSOR(xf, 3)
|
355
|
+
FLAG_ACCESSOR(pf, 2)
|
356
|
+
FLAG_ACCESSOR(nf, 1)
|
357
|
+
FLAG_ACCESSOR(cf, 0)
|
358
|
+
|
359
|
+
#undef FLAG_ACCESSOR
|
360
|
+
|
361
|
+
|
362
|
+
/* MARK: - Methods */
|
363
|
+
|
364
|
+
static VALUE Z80__power(VALUE self, VALUE state)
|
365
|
+
{
|
366
|
+
GET_Z80;
|
367
|
+
z80_power(z80, RB_TEST(state));
|
368
|
+
return self;
|
369
|
+
}
|
370
|
+
|
371
|
+
|
372
|
+
static VALUE Z80__instant_reset(VALUE self)
|
373
|
+
{
|
374
|
+
GET_Z80;
|
375
|
+
z80_instant_reset(z80);
|
376
|
+
return self;
|
377
|
+
}
|
378
|
+
|
379
|
+
|
380
|
+
static VALUE Z80__int(VALUE self, VALUE state)
|
381
|
+
{
|
382
|
+
GET_Z80;
|
383
|
+
z80_int(z80, RB_TEST(state));
|
384
|
+
return self;
|
385
|
+
}
|
386
|
+
|
387
|
+
|
388
|
+
static VALUE Z80__nmi(VALUE self)
|
389
|
+
{
|
390
|
+
GET_Z80;
|
391
|
+
z80_nmi(z80);
|
392
|
+
return self;
|
393
|
+
}
|
394
|
+
|
395
|
+
|
396
|
+
static VALUE Z80__execute(VALUE self, VALUE cycles)
|
397
|
+
{
|
398
|
+
GET_Z80;
|
399
|
+
return SIZET2NUM(z80_execute(z80, NUM2SIZET(cycles)));
|
400
|
+
}
|
401
|
+
|
402
|
+
|
403
|
+
static VALUE Z80__run(VALUE self, VALUE cycles)
|
404
|
+
{
|
405
|
+
GET_Z80;
|
406
|
+
return SIZET2NUM(z80_run(z80, NUM2SIZET(cycles)));
|
407
|
+
}
|
408
|
+
|
409
|
+
|
410
|
+
static VALUE Z80__terminate(VALUE self)
|
411
|
+
{
|
412
|
+
GET_Z80;
|
413
|
+
z80_break(z80);
|
414
|
+
return self;
|
415
|
+
}
|
416
|
+
|
417
|
+
|
418
|
+
static VALUE Z80__full_r(VALUE self)
|
419
|
+
{
|
420
|
+
GET_Z80;
|
421
|
+
return UINT2NUM(z80_r(z80));
|
422
|
+
}
|
423
|
+
|
424
|
+
|
425
|
+
static VALUE Z80__refresh_address(VALUE self)
|
426
|
+
{
|
427
|
+
GET_Z80;
|
428
|
+
return UINT2NUM(z80_refresh_address(z80));
|
429
|
+
}
|
430
|
+
|
431
|
+
|
432
|
+
static VALUE Z80__in_cycle(VALUE self)
|
433
|
+
{
|
434
|
+
GET_Z80;
|
435
|
+
return UINT2NUM(z80_in_cycle(z80));
|
436
|
+
}
|
437
|
+
|
438
|
+
|
439
|
+
static VALUE Z80__out_cycle(VALUE self)
|
440
|
+
{
|
441
|
+
GET_Z80;
|
442
|
+
return UINT2NUM(z80_out_cycle(z80));
|
443
|
+
}
|
444
|
+
|
445
|
+
|
446
|
+
static struct {char const* name; zuint offset;} const
|
447
|
+
|
448
|
+
members_16[] = {
|
449
|
+
{"memptr", Z_MEMBER_OFFSET(Z80, memptr )},
|
450
|
+
{"pc", Z_MEMBER_OFFSET(Z80, pc )},
|
451
|
+
{"sp", Z_MEMBER_OFFSET(Z80, sp )},
|
452
|
+
{"xy", Z_MEMBER_OFFSET(Z80, xy )},
|
453
|
+
{"ix", Z_MEMBER_OFFSET(Z80, ix_iy[0])},
|
454
|
+
{"iy", Z_MEMBER_OFFSET(Z80, ix_iy[1])},
|
455
|
+
{"af", Z_MEMBER_OFFSET(Z80, af )},
|
456
|
+
{"bc", Z_MEMBER_OFFSET(Z80, bc )},
|
457
|
+
{"de", Z_MEMBER_OFFSET(Z80, de )},
|
458
|
+
{"hl", Z_MEMBER_OFFSET(Z80, hl )},
|
459
|
+
{"af_", Z_MEMBER_OFFSET(Z80, af_ )},
|
460
|
+
{"bc_", Z_MEMBER_OFFSET(Z80, bc_ )},
|
461
|
+
{"de_", Z_MEMBER_OFFSET(Z80, de_ )},
|
462
|
+
{"hl_", Z_MEMBER_OFFSET(Z80, hl_ )}
|
463
|
+
},
|
464
|
+
|
465
|
+
members_8[] = {
|
466
|
+
{"r", Z_MEMBER_OFFSET(Z80, r )},
|
467
|
+
{"i", Z_MEMBER_OFFSET(Z80, i )},
|
468
|
+
{"r7", Z_MEMBER_OFFSET(Z80, r7 )},
|
469
|
+
{"im", Z_MEMBER_OFFSET(Z80, im )},
|
470
|
+
{"request", Z_MEMBER_OFFSET(Z80, request )},
|
471
|
+
{"resume", Z_MEMBER_OFFSET(Z80, resume )},
|
472
|
+
{"iff1", Z_MEMBER_OFFSET(Z80, iff1 )},
|
473
|
+
{"iff2", Z_MEMBER_OFFSET(Z80, iff2 )},
|
474
|
+
{"q", Z_MEMBER_OFFSET(Z80, q )},
|
475
|
+
{"options", Z_MEMBER_OFFSET(Z80, options )},
|
476
|
+
{"int_line", Z_MEMBER_OFFSET(Z80, int_line )},
|
477
|
+
{"halt_line", Z_MEMBER_OFFSET(Z80, halt_line)}
|
478
|
+
};
|
479
|
+
|
480
|
+
|
481
|
+
static VALUE Z80__to_h(VALUE self)
|
482
|
+
{
|
483
|
+
VALUE hash = rb_hash_new();
|
484
|
+
VALUE kv[(Z_ARRAY_SIZE(members_16) + Z_ARRAY_SIZE(members_8)) * 2];
|
485
|
+
int i = 0, j;
|
486
|
+
GET_Z80;
|
487
|
+
|
488
|
+
for (j = 0; j < Z_ARRAY_SIZE(members_16);)
|
489
|
+
{
|
490
|
+
kv[i++] = rb_id2sym(rb_intern(members_16[j].name));
|
491
|
+
kv[i++] = UINT2NUM(*(zuint16 *)(void *)((char *)z80 + members_16[j++].offset));
|
492
|
+
}
|
493
|
+
|
494
|
+
for (j = 0; j < Z_ARRAY_SIZE(members_8);)
|
495
|
+
{
|
496
|
+
kv[i++] = rb_id2sym(rb_intern(members_8[j].name));
|
497
|
+
kv[i++] = UINT2NUM(*((zuint8 *)z80 + members_8[j++].offset));
|
498
|
+
}
|
499
|
+
|
500
|
+
rb_hash_bulk_insert_into_st_table(Z_ARRAY_SIZE(kv), kv, hash);
|
501
|
+
return hash;
|
502
|
+
}
|
503
|
+
|
504
|
+
|
505
|
+
static char const one_hyphen[2] = "1-";
|
506
|
+
|
507
|
+
|
508
|
+
static VALUE Z80__print(VALUE self)
|
509
|
+
{
|
510
|
+
zuint8 f;
|
511
|
+
GET_Z80;
|
512
|
+
f = Z80_F(*z80);
|
513
|
+
|
514
|
+
/*
|
515
|
+
PC 0000 AF 0000 AF' 0000 IX 0000
|
516
|
+
SP 0000 BC 0000 BC' 0000 IY 0000
|
517
|
+
IR 0000 DE 0000 DE' 0000 XY 0000
|
518
|
+
WZ 0000 HL 0000 HL' 0000 Q 00
|
519
|
+
S Z Y H X P N C IFF1 0 IM 0 EI 1
|
520
|
+
- - 1 - - 1 - - IFF2 0 R7 0 RI 1
|
521
|
+
/INT high RS 00 data 00 00 00 00
|
522
|
+
/HALT high RQ 00 data 00 00 00 00*/
|
523
|
+
|
524
|
+
printf( "PC %04" PRIX16 " AF %04" PRIX16 " AF' %04" PRIX16 " IX %04" PRIX16 "\n"
|
525
|
+
"SP %04" PRIX16 " BC %04" PRIX16 " BC' %04" PRIX16 " IY %04" PRIX16 "\n"
|
526
|
+
"IR %02" PRIX8 "%02" PRIX8 " DE %04" PRIX16 " DE' %04" PRIX16 " XY %04" PRIX16 "\n"
|
527
|
+
"WZ %04" PRIX16 " HL %04" PRIX16 " HL' %04" PRIX16 " Q %02" PRIX8 "\n"
|
528
|
+
"S Z Y H X P N C" " IFF1 %" PRIu8 " IM %" PRIu8 " EI %" PRIu8 "\n"
|
529
|
+
"%c %c %c %c %c %c %c %c IFF2 %" PRIu8 " R7 %" PRIu8 " RI %" PRIu8 "\n",
|
530
|
+
Z80_PC(*z80), Z80_AF(*z80), Z80_AF_(*z80), Z80_IX(*z80),
|
531
|
+
Z80_SP(*z80), Z80_BC(*z80), Z80_BC_(*z80), Z80_IY(*z80),
|
532
|
+
z80->i, z80->r, Z80_DE(*z80), Z80_DE_(*z80), Z80_IX(*z80),
|
533
|
+
Z80_MEMPTR(*z80), Z80_HL(*z80), Z80_HL_(*z80), z80->q,
|
534
|
+
z80->iff1, z80->im, '\0',
|
535
|
+
one_hyphen[!(f & Z80_SF)],
|
536
|
+
one_hyphen[!(f & Z80_ZF)],
|
537
|
+
one_hyphen[!(f & Z80_YF)],
|
538
|
+
one_hyphen[!(f & Z80_HF)],
|
539
|
+
one_hyphen[!(f & Z80_XF)],
|
540
|
+
one_hyphen[!(f & Z80_PF)],
|
541
|
+
one_hyphen[!(f & Z80_NF)],
|
542
|
+
one_hyphen[!(f & Z80_CF)],
|
543
|
+
z80->iff2, z80->r7 >> 7, '\0');
|
544
|
+
|
545
|
+
return Qnil;
|
546
|
+
}
|
547
|
+
|
548
|
+
|
549
|
+
/* MARK: - Object Lifecycle */
|
550
|
+
|
551
|
+
static void Z80__mark(Z80 *z80)
|
552
|
+
{
|
553
|
+
VALUE *externals = z80->context;
|
554
|
+
|
555
|
+
for (int i = 18; i;) if (externals[--i] != Qnil)
|
556
|
+
rb_gc_mark_movable(externals[i]);
|
557
|
+
}
|
558
|
+
|
559
|
+
|
560
|
+
static void Z80__free(Z80 *z80)
|
561
|
+
{
|
562
|
+
free(z80->context);
|
563
|
+
xfree(z80);
|
564
|
+
}
|
565
|
+
|
566
|
+
|
567
|
+
static size_t Z80__memsize(const void *z80)
|
568
|
+
{return sizeof(Z80) + sizeof(VALUE[18]);}
|
569
|
+
|
570
|
+
|
571
|
+
static void Z80__compact(Z80 *z80)
|
572
|
+
{
|
573
|
+
VALUE *externals = z80->context;
|
574
|
+
|
575
|
+
for (int i = 18; i;) if (externals[--i] != Qnil)
|
576
|
+
externals[i] = rb_gc_location(externals[i]);
|
577
|
+
}
|
578
|
+
|
579
|
+
|
580
|
+
static rb_data_type_t const z80_data_type = {
|
581
|
+
.wrap_struct_name = "z80",
|
582
|
+
.function = {
|
583
|
+
.dmark = (void (*)(void *))Z80__mark,
|
584
|
+
.dfree = (void (*)(void *))Z80__free,
|
585
|
+
.dsize = NULL,
|
586
|
+
.dcompact = (void (*)(void *))Z80__compact
|
587
|
+
},
|
588
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
589
|
+
};
|
590
|
+
|
591
|
+
|
592
|
+
static VALUE Z80__alloc(VALUE klass)
|
593
|
+
{
|
594
|
+
Z80 *z80;
|
595
|
+
VALUE object = TypedData_Make_Struct(klass, Z80, &z80_data_type, z80);
|
596
|
+
VALUE *externals = z80->context = malloc(sizeof(VALUE[18]));
|
597
|
+
|
598
|
+
for (int i = 18; i;) externals[--i] = Qnil;
|
599
|
+
|
600
|
+
z80->options = Z80_MODEL_ZILOG_NMOS;
|
601
|
+
z80->fetch_opcode =
|
602
|
+
z80->fetch =
|
603
|
+
z80->read = dummy_read;
|
604
|
+
z80->write =
|
605
|
+
z80->out = dummy_write;
|
606
|
+
z80->in = dummy_in;
|
607
|
+
z80->halt = NULL;
|
608
|
+
z80->nop =
|
609
|
+
z80->nmia =
|
610
|
+
z80->inta =
|
611
|
+
z80->int_fetch =
|
612
|
+
z80->hook = NULL;
|
613
|
+
z80->ld_i_a =
|
614
|
+
z80->ld_r_a =
|
615
|
+
z80->reti =
|
616
|
+
z80->retn = NULL;
|
617
|
+
z80->illegal = NULL;
|
618
|
+
|
619
|
+
return object;
|
620
|
+
}
|
621
|
+
|
622
|
+
|
623
|
+
/* Library Initialization */
|
624
|
+
|
625
|
+
void Init_z80(void)
|
626
|
+
{
|
627
|
+
VALUE module, klass = rb_const_get(rb_cObject, rb_intern("Z80"));
|
628
|
+
|
629
|
+
rb_define_alloc_func(klass, Z80__alloc);
|
630
|
+
|
631
|
+
rb_define_const(klass, "MAXIMUM_CYCLES", ULL2NUM(Z80_MAXIMUM_CYCLES ));
|
632
|
+
rb_define_const(klass, "MAXIMUM_CYCLES_PER_STEP", UINT2NUM(Z80_MAXIMUM_CYCLES_PER_STEP));
|
633
|
+
rb_define_const(klass, "HOOK", UINT2NUM(Z80_HOOK ));
|
634
|
+
|
635
|
+
rb_define_const(klass, "SF", UINT2NUM(Z80_SF));
|
636
|
+
rb_define_const(klass, "ZF", UINT2NUM(Z80_ZF));
|
637
|
+
rb_define_const(klass, "YF", UINT2NUM(Z80_YF));
|
638
|
+
rb_define_const(klass, "HF", UINT2NUM(Z80_HF));
|
639
|
+
rb_define_const(klass, "XF", UINT2NUM(Z80_XF));
|
640
|
+
rb_define_const(klass, "PF", UINT2NUM(Z80_PF));
|
641
|
+
rb_define_const(klass, "NF", UINT2NUM(Z80_NF));
|
642
|
+
rb_define_const(klass, "CF", UINT2NUM(Z80_CF));
|
643
|
+
|
644
|
+
module = rb_define_module_under(klass, "Option");
|
645
|
+
|
646
|
+
rb_define_const(module, "OUT_VC_255", UINT2NUM(Z80_OPTION_OUT_VC_255 ));
|
647
|
+
rb_define_const(module, "LD_A_IR_BUG", UINT2NUM(Z80_OPTION_LD_A_IR_BUG ));
|
648
|
+
rb_define_const(module, "HALT_SKIP", UINT2NUM(Z80_OPTION_HALT_SKIP ));
|
649
|
+
rb_define_const(module, "XQ", UINT2NUM(Z80_OPTION_XQ ));
|
650
|
+
rb_define_const(module, "IM0_RETX_NOTIFICATIONS", UINT2NUM(Z80_OPTION_IM0_RETX_NOTIFICATIONS));
|
651
|
+
rb_define_const(module, "YQ", UINT2NUM(Z80_OPTION_YQ ));
|
652
|
+
|
653
|
+
module = rb_define_module_under(module, "Model");
|
654
|
+
|
655
|
+
rb_define_const(module, "ZILOG_NMOS", UINT2NUM(Z80_MODEL_ZILOG_NMOS));
|
656
|
+
rb_define_const(module, "ZILOG_CMOS", UINT2NUM(Z80_MODEL_ZILOG_CMOS));
|
657
|
+
rb_define_const(module, "NEC_NMOS", UINT2NUM(Z80_MODEL_NEC_NMOS ));
|
658
|
+
rb_define_const(module, "ST_CMOS", UINT2NUM(Z80_MODEL_ST_CMOS ));
|
659
|
+
|
660
|
+
module = rb_define_module_under(klass, "Request");
|
661
|
+
|
662
|
+
rb_define_const(module, "REJECT_NMI", UINT2NUM(Z80_REQUEST_REJECT_NMI ));
|
663
|
+
rb_define_const(module, "NMI", UINT2NUM(Z80_REQUEST_NMI ));
|
664
|
+
rb_define_const(module, "INT", UINT2NUM(Z80_REQUEST_INT ));
|
665
|
+
/* rb_define_const(module, "SPECIAL_RESET", UINT2NUM(Z80_REQUEST_SPECIAL_RESET));*/
|
666
|
+
|
667
|
+
module = rb_define_module_under(klass, "Resume");
|
668
|
+
|
669
|
+
rb_define_const(module, "HALT", UINT2NUM(Z80_RESUME_HALT ));
|
670
|
+
rb_define_const(module, "XY", UINT2NUM(Z80_RESUME_XY ));
|
671
|
+
rb_define_const(module, "IM0_XY", UINT2NUM(Z80_RESUME_IM0_XY));
|
672
|
+
|
673
|
+
# define DEFINE_ACCESSOR(name, getter_arity) \
|
674
|
+
rb_define_method(klass, #name, Z80__##name, getter_arity); \
|
675
|
+
rb_define_method(klass, #name "=", Z80__set_##name, 1);
|
676
|
+
|
677
|
+
DEFINE_ACCESSOR(fetch_opcode, 0)
|
678
|
+
DEFINE_ACCESSOR(fetch, 0)
|
679
|
+
DEFINE_ACCESSOR(read, 0)
|
680
|
+
DEFINE_ACCESSOR(write, 0)
|
681
|
+
DEFINE_ACCESSOR(in, 0)
|
682
|
+
DEFINE_ACCESSOR(out, 0)
|
683
|
+
DEFINE_ACCESSOR(halt, 0)
|
684
|
+
DEFINE_ACCESSOR(nop, 0)
|
685
|
+
DEFINE_ACCESSOR(nmia, 0)
|
686
|
+
DEFINE_ACCESSOR(inta, 0)
|
687
|
+
DEFINE_ACCESSOR(int_fetch, 0)
|
688
|
+
DEFINE_ACCESSOR(ld_i_a, 0)
|
689
|
+
DEFINE_ACCESSOR(ld_r_a, 0)
|
690
|
+
DEFINE_ACCESSOR(reti, 0)
|
691
|
+
DEFINE_ACCESSOR(retn, 0)
|
692
|
+
DEFINE_ACCESSOR(hook, 0)
|
693
|
+
DEFINE_ACCESSOR(illegal, 0)
|
694
|
+
DEFINE_ACCESSOR(context, 0)
|
695
|
+
DEFINE_ACCESSOR(cycles, 0)
|
696
|
+
DEFINE_ACCESSOR(cycle_limit, 0)
|
697
|
+
DEFINE_ACCESSOR(memptr, 0)
|
698
|
+
DEFINE_ACCESSOR(pc, 0)
|
699
|
+
DEFINE_ACCESSOR(sp, 0)
|
700
|
+
DEFINE_ACCESSOR(xy, 0)
|
701
|
+
DEFINE_ACCESSOR(ix, 0)
|
702
|
+
DEFINE_ACCESSOR(iy, 0)
|
703
|
+
DEFINE_ACCESSOR(af, 0)
|
704
|
+
DEFINE_ACCESSOR(bc, 0)
|
705
|
+
DEFINE_ACCESSOR(de, 0)
|
706
|
+
DEFINE_ACCESSOR(hl, 0)
|
707
|
+
DEFINE_ACCESSOR(af_, 0)
|
708
|
+
DEFINE_ACCESSOR(bc_, 0)
|
709
|
+
DEFINE_ACCESSOR(de_, 0)
|
710
|
+
DEFINE_ACCESSOR(hl_, 0)
|
711
|
+
DEFINE_ACCESSOR(memptrh, 0)
|
712
|
+
DEFINE_ACCESSOR(memptrl, 0)
|
713
|
+
DEFINE_ACCESSOR(pch, 0)
|
714
|
+
DEFINE_ACCESSOR(pcl, 0)
|
715
|
+
DEFINE_ACCESSOR(sph, 0)
|
716
|
+
DEFINE_ACCESSOR(spl, 0)
|
717
|
+
DEFINE_ACCESSOR(xyh, 0)
|
718
|
+
DEFINE_ACCESSOR(yl, 0)
|
719
|
+
DEFINE_ACCESSOR(ixh, 0)
|
720
|
+
DEFINE_ACCESSOR(ixl, 0)
|
721
|
+
DEFINE_ACCESSOR(iyh, 0)
|
722
|
+
DEFINE_ACCESSOR(iyl, 0)
|
723
|
+
DEFINE_ACCESSOR(a, 0)
|
724
|
+
DEFINE_ACCESSOR(f, 0)
|
725
|
+
DEFINE_ACCESSOR(b, 0)
|
726
|
+
DEFINE_ACCESSOR(c, 0)
|
727
|
+
DEFINE_ACCESSOR(d, 0)
|
728
|
+
DEFINE_ACCESSOR(e, 0)
|
729
|
+
DEFINE_ACCESSOR(h, 0)
|
730
|
+
DEFINE_ACCESSOR(l, 0)
|
731
|
+
DEFINE_ACCESSOR(a_, 0)
|
732
|
+
DEFINE_ACCESSOR(f_, 0)
|
733
|
+
DEFINE_ACCESSOR(b_, 0)
|
734
|
+
DEFINE_ACCESSOR(c_, 0)
|
735
|
+
DEFINE_ACCESSOR(d_, 0)
|
736
|
+
DEFINE_ACCESSOR(e_, 0)
|
737
|
+
DEFINE_ACCESSOR(h_, 0)
|
738
|
+
DEFINE_ACCESSOR(l_, 0)
|
739
|
+
DEFINE_ACCESSOR(r, 0)
|
740
|
+
DEFINE_ACCESSOR(i, 0)
|
741
|
+
DEFINE_ACCESSOR(r7, 0)
|
742
|
+
DEFINE_ACCESSOR(im, 0)
|
743
|
+
DEFINE_ACCESSOR(request, 0)
|
744
|
+
DEFINE_ACCESSOR(resume, 0)
|
745
|
+
DEFINE_ACCESSOR(iff1, 0)
|
746
|
+
DEFINE_ACCESSOR(iff2, 0)
|
747
|
+
DEFINE_ACCESSOR(q, 0)
|
748
|
+
DEFINE_ACCESSOR(options, 0)
|
749
|
+
DEFINE_ACCESSOR(int_line, 0)
|
750
|
+
DEFINE_ACCESSOR(halt_line, 0)
|
751
|
+
DEFINE_ACCESSOR(sf, 0)
|
752
|
+
DEFINE_ACCESSOR(zf, 0)
|
753
|
+
DEFINE_ACCESSOR(yf, 0)
|
754
|
+
DEFINE_ACCESSOR(hf, 0)
|
755
|
+
DEFINE_ACCESSOR(xf, 0)
|
756
|
+
DEFINE_ACCESSOR(pf, 0)
|
757
|
+
DEFINE_ACCESSOR(nf, 0)
|
758
|
+
DEFINE_ACCESSOR(cf, 0)
|
759
|
+
|
760
|
+
# undef DEFINE_ACCESSOR
|
761
|
+
|
762
|
+
rb_define_method(klass, "power", Z80__power, 1);
|
763
|
+
rb_define_method(klass, "instant_reset", Z80__instant_reset, 0);
|
764
|
+
rb_define_method(klass, "int", Z80__int, 1);
|
765
|
+
rb_define_method(klass, "nmi", Z80__nmi, 0);
|
766
|
+
rb_define_method(klass, "execute", Z80__execute, 1);
|
767
|
+
rb_define_method(klass, "run", Z80__run, 1);
|
768
|
+
rb_define_method(klass, "terminate", Z80__terminate, 0);
|
769
|
+
rb_define_method(klass, "full_r", Z80__refresh_address, 0);
|
770
|
+
rb_define_method(klass, "refresh_address", Z80__refresh_address, 0);
|
771
|
+
rb_define_method(klass, "in_cycle", Z80__in_cycle, 0);
|
772
|
+
rb_define_method(klass, "out_cycle", Z80__out_cycle, 0);
|
773
|
+
rb_define_method(klass, "to_h", Z80__to_h, 0);
|
774
|
+
rb_define_method(klass, "print", Z80__print, 0);
|
775
|
+
/* rb_define_method(klass, "to_s", Z80__to_s, 0);*/
|
776
|
+
|
777
|
+
rb_define_alias(klass, "t", "cycles" );
|
778
|
+
rb_define_alias(klass, "t=", "cycles=" );
|
779
|
+
rb_define_alias(klass, "wz", "memptr" );
|
780
|
+
rb_define_alias(klass, "wz=", "memptr=" );
|
781
|
+
rb_define_alias(klass, "w", "memptrh" );
|
782
|
+
rb_define_alias(klass, "w=", "memptrh=");
|
783
|
+
rb_define_alias(klass, "z", "memptrl" );
|
784
|
+
rb_define_alias(klass, "z=", "memptrl=");
|
785
|
+
rb_define_alias(klass, "vf", "pf" );
|
786
|
+
rb_define_alias(klass, "vf=", "pf=" );
|
787
|
+
rb_define_alias(klass, "state", "to_h" );
|
788
|
+
}
|
789
|
+
|
790
|
+
|
791
|
+
/* z80.c EOF */
|
data/lib/z80/version.rb
ADDED
data/lib/z80.rb
ADDED
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: z80
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Manuel Sainz de Baranda y Goñi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-12-24 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: 'Z80-Ruby is a Ruby binding for the Zilog Z80 CPU emulator (https://github.com/redcode/Z80).
|
14
|
+
It is ideal for analysis, hacking, testing and debugging. All from the comfort of
|
15
|
+
Ruby.
|
16
|
+
|
17
|
+
'
|
18
|
+
email: manuel@zxe.io
|
19
|
+
executables: []
|
20
|
+
extensions:
|
21
|
+
- ext/z80/extconf.rb
|
22
|
+
extra_rdoc_files: []
|
23
|
+
files:
|
24
|
+
- ".editorconfig"
|
25
|
+
- ".github/FUNDING.yml"
|
26
|
+
- ".gitignore"
|
27
|
+
- CHANGELOG.md
|
28
|
+
- LICENSE-0BSD
|
29
|
+
- README.md
|
30
|
+
- Rakefile
|
31
|
+
- ext/z80/extconf.rb
|
32
|
+
- ext/z80/z80.c
|
33
|
+
- lib/z80.rb
|
34
|
+
- lib/z80/version.rb
|
35
|
+
homepage: https://zxe.io/software/Z80
|
36
|
+
licenses:
|
37
|
+
- 0BSD
|
38
|
+
metadata:
|
39
|
+
bug_tracker_uri: https://github.com/redcode/Z80-Ruby/issues
|
40
|
+
changelog_uri: https://github.com/redcode/Z80-Ruby/blob/master/CHANGELOG.md
|
41
|
+
homepage_uri: https://zxe.io/software/Z80
|
42
|
+
source_code_uri: https://github.com/redcode/Z80-Ruby
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubygems_version: 3.5.0.dev
|
59
|
+
signing_key:
|
60
|
+
specification_version: 4
|
61
|
+
summary: Ruby binding for the Z80 library.
|
62
|
+
test_files: []
|