retrograph 0.5-x86-mswin32
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +19 -0
- data/README +264 -0
- data/Rakefile +71 -0
- data/ext/retrograph/extconf.rb +6 -0
- data/ext/retrograph/retrograph.c +324 -0
- data/lib/retrograph/sdl.rb +26 -0
- data/lib/retrograph.rb +29 -0
- data/lib/retrograph.so +0 -0
- data/spec/image-specs/color-blue-bits.case +2 -0
- data/spec/image-specs/color-blue-bits.png +0 -0
- data/spec/image-specs/color-green-bits.case +2 -0
- data/spec/image-specs/color-green-bits.png +0 -0
- data/spec/image-specs/color-red-bits.case +2 -0
- data/spec/image-specs/color-red-bits.png +0 -0
- data/spec/image-specs/default-screen-black.case +1 -0
- data/spec/image-specs/default-screen-black.png +0 -0
- data/spec/image-specs/default-screen-color-0.case +2 -0
- data/spec/image-specs/default-screen-color-0.png +0 -0
- data/spec/image-specs/scroll-mode1.case +7 -0
- data/spec/image-specs/scroll-mode1.png +0 -0
- data/spec/image-specs/scroll-mode2.case +10 -0
- data/spec/image-specs/scroll-mode2.png +0 -0
- data/spec/image-specs/scroll-mode3.case +10 -0
- data/spec/image-specs/scroll-mode3.png +0 -0
- data/spec/image-specs/sprites-doubling.case +5 -0
- data/spec/image-specs/sprites-doubling.png +0 -0
- data/spec/image-specs/sprites-mode2.case +8 -0
- data/spec/image-specs/sprites-mode2.png +0 -0
- data/spec/image-specs/sprites-mode3.case +8 -0
- data/spec/image-specs/sprites-mode3.png +0 -0
- data/spec/image-specs/sprites-ordering.case +5 -0
- data/spec/image-specs/sprites-ordering.png +0 -0
- data/spec/image-specs/text-colors.case +5 -0
- data/spec/image-specs/text-colors.png +0 -0
- data/spec/image-specs/tile-attributes-mode2.case +9 -0
- data/spec/image-specs/tile-attributes-mode2.png +0 -0
- data/spec/image-specs/tile-attributes-mode3.case +9 -0
- data/spec/image-specs/tile-attributes-mode3.png +0 -0
- data/spec/retrograph_spec.rb +134 -0
- data/src/retrograph.c +697 -0
- data/src/retrograph.h +67 -0
- metadata +99 -0
data/COPYING
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2009 MenTaLguY <mental@rydia.net>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
== What is Retrograph? ==
|
2
|
+
|
3
|
+
Retrograph is a Ruby library which emulates the video
|
4
|
+
display unit from a hypothetical late-80s 8-bit game
|
5
|
+
console. It is similar in capability to the Sega Master
|
6
|
+
System's VDP, with some additional features and direct
|
7
|
+
support for text modes.
|
8
|
+
|
9
|
+
Retrograph currently supports Ruby/SDL for output; other
|
10
|
+
output targets may be added in the future.
|
11
|
+
|
12
|
+
== Feature Overview ==
|
13
|
+
|
14
|
+
* 256x244 pixel display
|
15
|
+
* 16k VRAM
|
16
|
+
* 64 colors total
|
17
|
+
* max 31 simultaneous colors (16 background, 15 sprite)
|
18
|
+
* 40x25 and 32x28 text modes
|
19
|
+
* 32x32 tile background, 8x8 pixel tiles
|
20
|
+
* 64 8x8 sprites, max 8 per scanline
|
21
|
+
|
22
|
+
== Usage ==
|
23
|
+
|
24
|
+
Retrograph's simulated Video Display Unit (or VDU) is
|
25
|
+
represented by an instance of the Retrograph::VDU class.
|
26
|
+
You can interact with the VDU by writing byte strings
|
27
|
+
into its memory using Retrograph::VDU#write, which takes
|
28
|
+
a start address (in the VDU's 16-bit address space) and
|
29
|
+
a string to write. The method Retrograph::VDU#write_byte
|
30
|
+
is also provided for writing individual bytes.
|
31
|
+
|
32
|
+
(See the end of this document for a map of registers and
|
33
|
+
memory locations within the VDU's address space.)
|
34
|
+
|
35
|
+
To render a frame of video with SDL, use
|
36
|
+
Retrograph::VDU#render_frame_sdl, which returns an
|
37
|
+
SDL::Surface containing the VDU output. The returned
|
38
|
+
surface remains valid until the next call to
|
39
|
+
Retrograph::VDU#render_frame_sdl on the same VDU instance.
|
40
|
+
|
41
|
+
Scanline-based effects are possible if you provide a block
|
42
|
+
to Retrograph::VDU#render_frame_sdl. Ordinarily, rendering
|
43
|
+
will not begin until the block completes, but within the
|
44
|
+
block you can call Retrograph::VDU#wait_scanlines to render
|
45
|
+
a given number of scanlines before continuing. This allows
|
46
|
+
you to make changes to the VDU state part-way through
|
47
|
+
rendering a frame.
|
48
|
+
|
49
|
+
For example, if we wanted palette entry 0 to be blue within
|
50
|
+
the top half of the display and red within the bottom half
|
51
|
+
of the display, we could write:
|
52
|
+
|
53
|
+
require 'retrograph/sdl'
|
54
|
+
|
55
|
+
output = vdu.render_frame_sdl do
|
56
|
+
vdu.write_byte(0x7f00, 0x03)
|
57
|
+
vdu.wait_scanlines(Retrograph::DISPLAY_HEIGHT/2)
|
58
|
+
vdu.write_byte(0x7f00, 0x30)
|
59
|
+
end
|
60
|
+
|
61
|
+
== Important Notes ==
|
62
|
+
|
63
|
+
Nothing is displayed by default. To be shown, the
|
64
|
+
background layer (and the sprite, in graphical modes) must
|
65
|
+
be explicitly enabled by setting bit 2 (bit 3 for sprites)
|
66
|
+
of register $7FF8.
|
67
|
+
|
68
|
+
Patterns and name tables may overlap in memory (which is
|
69
|
+
in fact unavoidable in graphics modes); typically you
|
70
|
+
cannot have simultaneously valid pattern and name data at
|
71
|
+
the same location, so in such cases you will need to
|
72
|
+
sacrifice particular name or pattern entries.
|
73
|
+
|
74
|
+
In text modes, you will need to load your own font into the
|
75
|
+
pattern table; the VDU does not provide one by default.
|
76
|
+
|
77
|
+
Sprites patterns are always 4bpp, even in the 2bpp mode.
|
78
|
+
In Graphics A, one half of VRAM is occupied by 512 2bpp
|
79
|
+
patterns (for the background), and the other half by 256
|
80
|
+
4bpp patterns for sprites. In Graphics B, the entirety
|
81
|
+
of VRAM is occupied by just 512 4bpp patterns, with the
|
82
|
+
upper half of those available to sprites.
|
83
|
+
|
84
|
+
When scrolling horizontally, the leftmost background
|
85
|
+
column will not be displayed if it is partway offscreen,
|
86
|
+
a (mis)feature shared with the Sega Master System.
|
87
|
+
|
88
|
+
Unlike most authentic 8-bit systems, any register or memory
|
89
|
+
location may be changed in between scanlines, including the
|
90
|
+
vertical scroll register and even the video mode. This
|
91
|
+
permits fairly powerful split-screen effects.
|
92
|
+
|
93
|
+
Also unlike most authentic 8-bit systems, Retrograph uses a
|
94
|
+
packed rather than a planar representation for pattern
|
95
|
+
data. Additionally, within each byte of pattern data, the
|
96
|
+
leftmost pixels are in the least significant position,
|
97
|
+
which may be backwards from what you expect.
|
98
|
+
|
99
|
+
Lastly, unlike a real 8-bit system, there is no deadline
|
100
|
+
imposed on code running in between scanlines or in between
|
101
|
+
frames. On real hardware, if you have code running
|
102
|
+
in between scanlines (that is, during horizontal retrace)
|
103
|
+
you will only have a short period of time available before
|
104
|
+
the next scanline will begin rendering, whether or not you
|
105
|
+
are finished what you are doing. However, such a
|
106
|
+
limitation would be very difficult to recreate in Ruby.
|
107
|
+
|
108
|
+
== VDU Memory Map ==
|
109
|
+
|
110
|
+
$0000 - $3FFF: VRAM (16 kbytes)
|
111
|
+
$4000 - $7DFF: VRAM (mirror) (16 kbytes - 512 bytes)
|
112
|
+
$7E00 - $7EFF: Sprite Table* (256 bytes)
|
113
|
+
$7F00 - $7F0F: BG Palette (16 bytes)
|
114
|
+
$7F10 - $7F1F: BG/Sprite Palette* (16 bytes)
|
115
|
+
$7F20 - $7FF7: unused (216 bytes)
|
116
|
+
$7FF8 - $7FFF: VDU Registers (8 bytes)
|
117
|
+
$7FF8: Flags
|
118
|
+
4-7: unused
|
119
|
+
3: Enable Sprites*
|
120
|
+
2: Enable BG
|
121
|
+
0-1: Mode
|
122
|
+
00 - Text A (40x25)
|
123
|
+
01 - Text B (32x28)
|
124
|
+
10 - Graphics A (2bpp)
|
125
|
+
11 - Graphics B (4bpp)
|
126
|
+
$7FF9: Pattern Table Base
|
127
|
+
Text A + B:
|
128
|
+
addr = (base & 0b00110000) << 8
|
129
|
+
addr may be one of:
|
130
|
+
$0000, $1000, $2000, or $3000
|
131
|
+
Graphics A + B:
|
132
|
+
addr = (base & 0b00100000) << 8
|
133
|
+
addr may be one of:
|
134
|
+
$0000 or $2000
|
135
|
+
$7FFA: Name Table Base
|
136
|
+
addr = ((base & 0b00110000) |
|
137
|
+
0b00001000) << 8
|
138
|
+
addr may be one of:
|
139
|
+
$0800, $1800, $2800, or $3800
|
140
|
+
$7FFB: unused
|
141
|
+
$7FFC: BG scroll X*
|
142
|
+
$7FFD: BG scroll Y**
|
143
|
+
$7FFE-7FFF: unused
|
144
|
+
$8000 - $FFFF: mirror of $0000 - $7FFF
|
145
|
+
|
146
|
+
Notes:
|
147
|
+
*Ignored in text modes
|
148
|
+
**In text modes, the lower 3 bits of the vertical scroll
|
149
|
+
register are ignored and it wraps at 200 (Text A) or
|
150
|
+
224 (Text B)
|
151
|
+
|
152
|
+
=== Pattern Table ===
|
153
|
+
|
154
|
+
The starting address of the pattern table is determined by
|
155
|
+
register $7FF9.
|
156
|
+
|
157
|
+
The rows constituting each pattern in the table are given
|
158
|
+
in sequence from top to bottom. Within each byte, the
|
159
|
+
leftmost pixel is in the least significant position. For
|
160
|
+
example, for a 4bpp tile, the following row would display
|
161
|
+
with the pixels of color 1 on the *left* side, and the
|
162
|
+
pixels of color 0 on the *right* side:
|
163
|
+
|
164
|
+
0x00001111
|
165
|
+
|
166
|
+
(little-endian byte ordering is assumed)
|
167
|
+
|
168
|
+
Different display modes have different pattern bit depths:
|
169
|
+
|
170
|
+
Text A: 256 patterns * 8 bytes per pattern = 2k
|
171
|
+
6x8 pixel patterns
|
172
|
+
1 byte per pattern row, 1 bit per pixel
|
173
|
+
most significant 2 bits ignored
|
174
|
+
|
175
|
+
Text B: 256 patterns * 8 bytes per pattern = 2k
|
176
|
+
8x8 pixel patterns
|
177
|
+
1 byte per patern row, 1 bit per pixel
|
178
|
+
|
179
|
+
Graphics A: 512 bg patterns * 16 bytes per pattern +
|
180
|
+
256 sprite patterns * 32 bytes/pattern = 16k
|
181
|
+
8x8 pixel patterns
|
182
|
+
2 bytes/pattern row, 2 bits/pixel (bg)
|
183
|
+
4 bytes/pattern row, 4 bits/pixel (sprites)
|
184
|
+
|
185
|
+
Graphics B: 512 patterns * 32 bytes per pattern = 16k
|
186
|
+
8x8 pixel patterns
|
187
|
+
4 bytes/pattern row, 4 bits/pixel
|
188
|
+
last 256 patterns shared with sprites
|
189
|
+
|
190
|
+
=== Name Table ===
|
191
|
+
|
192
|
+
The starting address of the name table is determined by
|
193
|
+
register $7FFA.
|
194
|
+
|
195
|
+
Table sizes:
|
196
|
+
|
197
|
+
Text A: 40x25 characters * 2 bytes/character = 2000 bytes
|
198
|
+
Text B: 32x28 characters * 2 bytes/character = 1792 bytes
|
199
|
+
Graphics A + B: 32x32 tiles * 2 bytes per tile = 2k
|
200
|
+
|
201
|
+
Cell formats:
|
202
|
+
|
203
|
+
Text: pppppppp bbbbffff
|
204
|
+
p - pattern index
|
205
|
+
f - foreground color
|
206
|
+
b - background color
|
207
|
+
|
208
|
+
Graphics: pppppppp -bhvcccP
|
209
|
+
p - low byte of pattern index
|
210
|
+
P - high bit of pattern index
|
211
|
+
c - high bits of color
|
212
|
+
h - horizontal flip
|
213
|
+
v - vertical flip
|
214
|
+
b - background priority
|
215
|
+
|
216
|
+
In graphics modes, the color is computed by taking the high
|
217
|
+
color bits and oring them with the pattern color to get a
|
218
|
+
color in the range 0-31 (with the upper 16 colors coming
|
219
|
+
from the sprite palette):
|
220
|
+
|
221
|
+
color = pattern_color | (high_bits << 2)
|
222
|
+
|
223
|
+
(However, a pattern color of 0 is always transparent
|
224
|
+
regardless of the high color bits.)
|
225
|
+
|
226
|
+
=== Sprite Table ===
|
227
|
+
|
228
|
+
Sprite patterns start 2048 bytes after the pattern table
|
229
|
+
base address specified in register $7FF9. The sprite
|
230
|
+
table itself always begins at $7E00.
|
231
|
+
|
232
|
+
4 bytes * 64 sprites = 256 bytes
|
233
|
+
|
234
|
+
xxxxxxxx yyyyyyyy pppppppp --hvHVcc
|
235
|
+
|
236
|
+
x - X position (left edge)
|
237
|
+
y - Y position + 1 (top edge) (0 = hidden)
|
238
|
+
p - pattern index
|
239
|
+
h - horizontal flip
|
240
|
+
v - vertical flip
|
241
|
+
H - double size horizontally
|
242
|
+
V - double size vertically
|
243
|
+
c - high bits of color
|
244
|
+
|
245
|
+
Sprite colors are computed as follows:
|
246
|
+
|
247
|
+
color = pattern_color | (high_bits << 2) | 16;
|
248
|
+
|
249
|
+
(As with tiles, a pattern color of 0 is always transparent.)
|
250
|
+
|
251
|
+
=== Palette Table ===
|
252
|
+
|
253
|
+
The palette table always begins at $7F00.
|
254
|
+
|
255
|
+
1 byte * 32 colors (16 bg, 16 bg/sprite)
|
256
|
+
|
257
|
+
--rrggbb
|
258
|
+
|
259
|
+
r - red
|
260
|
+
g - green
|
261
|
+
b - blue
|
262
|
+
|
263
|
+
Note that the first sprite color (color 16) is effectively
|
264
|
+
never used.
|
data/Rakefile
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
begin
|
4
|
+
require 'rake/extensiontask'
|
5
|
+
have_rake_compiler = true
|
6
|
+
rescue LoadError
|
7
|
+
$stderr.puts "*** The rake-compiler gem is required to compile. ***"
|
8
|
+
have_rake_compiler = false
|
9
|
+
end
|
10
|
+
begin
|
11
|
+
require 'spec/rake/spectask'
|
12
|
+
have_rspec = true
|
13
|
+
rescue LoadError
|
14
|
+
$stderr.puts "*** The rspec gem is required to run specs. ***"
|
15
|
+
have_rspec = false
|
16
|
+
end
|
17
|
+
require 'rake/clean'
|
18
|
+
|
19
|
+
GEM_VERSION = '0.5'
|
20
|
+
|
21
|
+
CLEAN.include("spec/**/*-output.bmp")
|
22
|
+
CLOBBER.include("lib/1.8")
|
23
|
+
CLOBBER.include("lib/1.9")
|
24
|
+
|
25
|
+
task :clobber => [:clean]
|
26
|
+
|
27
|
+
gemspec = Gem::Specification.new do |gemspec|
|
28
|
+
gemspec.name = "retrograph"
|
29
|
+
gemspec.version = GEM_VERSION
|
30
|
+
gemspec.author = "MenTaLguY <mental@rydia.net>"
|
31
|
+
gemspec.summary = "Retrograph, a retrodisplay."
|
32
|
+
gemspec.description = <<EOS
|
33
|
+
Retrograph is a software scanline renderer which simulates a late-era 8-bit
|
34
|
+
display. Its graphical capabilities are similar to those of the Sega Master
|
35
|
+
System.
|
36
|
+
EOS
|
37
|
+
gemspec.homepage = "http://rubyforge.org/projects/retrograph"
|
38
|
+
gemspec.email = "mental@rydia.net"
|
39
|
+
gemspec.rubyforge_project = 'retrograph'
|
40
|
+
gemspec.files = FileList['Rakefile', 'README', 'COPYING',
|
41
|
+
'src/**/*.h', 'src/**/*.c', 'lib/**/*.rb',
|
42
|
+
'ext/**/extconf.rb', 'ext/**/*.c', 'ext/**/*.h',
|
43
|
+
'spec/**/*_spec.rb', 'spec/**/*.case',
|
44
|
+
'spec/**/*.png']
|
45
|
+
gemspec.extensions = FileList['ext/**/extconf.rb']
|
46
|
+
gemspec.require_paths = ['lib']
|
47
|
+
gemspec.platform = Gem::Platform::RUBY
|
48
|
+
end
|
49
|
+
|
50
|
+
Rake::GemPackageTask.new(gemspec) do |pkg|
|
51
|
+
pkg.need_tar = true
|
52
|
+
end
|
53
|
+
|
54
|
+
if have_rake_compiler
|
55
|
+
Rake::ExtensionTask.new('retrograph', gemspec) do |ext|
|
56
|
+
ext.cross_compile = true
|
57
|
+
ext.cross_platform = 'i386-mswin32'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
if have_rspec
|
62
|
+
Spec::Rake::SpecTask.new do |task|
|
63
|
+
task.ruby_opts << '-rrubygems'
|
64
|
+
task.libs << 'lib'
|
65
|
+
task.spec_files = FileList["spec/**/*_spec.rb"]
|
66
|
+
end
|
67
|
+
task :spec => [:compile]
|
68
|
+
task :default => [:clean, :spec]
|
69
|
+
elsif have_rake_compiler
|
70
|
+
task :default => [:clean, :compile]
|
71
|
+
end
|
@@ -0,0 +1,324 @@
|
|
1
|
+
/* retrograph
|
2
|
+
*
|
3
|
+
* Copyright (c) 2009 MenTaLguY <mental@rydia.net>
|
4
|
+
*
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
* of this software and associated documentation files (the "Software"), to deal
|
7
|
+
* in the Software without restriction, including without limitation the rights
|
8
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
* copies of the Software, and to permit persons to whom the Software is
|
10
|
+
* furnished to do so, subject to the following conditions:
|
11
|
+
*
|
12
|
+
* The above copyright notice and this permission notice shall be included in
|
13
|
+
* all copies or substantial portions of the Software.
|
14
|
+
*
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
* THE SOFTWARE.
|
22
|
+
*/
|
23
|
+
#include <ruby.h>
|
24
|
+
#ifdef RUBY_1_8
|
25
|
+
#include <intern.h>
|
26
|
+
#else
|
27
|
+
#include <ruby/intern.h>
|
28
|
+
#endif
|
29
|
+
#include <string.h>
|
30
|
+
|
31
|
+
#include "../../src/retrograph.h"
|
32
|
+
#include "../../src/retrograph.c"
|
33
|
+
|
34
|
+
#ifndef RSTRING_LEN
|
35
|
+
#define RSTRING_LEN(str) RSTRING(str)->len
|
36
|
+
#define RSTRING_PTR(str) RSTRING(str)->ptr
|
37
|
+
#endif
|
38
|
+
|
39
|
+
#define UPSCALE_FACTOR 2
|
40
|
+
#define UPSCALE_REPEAT(stmts) stmts; stmts;
|
41
|
+
#define UPSCALE_REPEAT_MINUS1(stmts) stmts;
|
42
|
+
|
43
|
+
/* First few fields of SDL_Surface */
|
44
|
+
typedef struct surface_header_tag {
|
45
|
+
uint32_t flags;
|
46
|
+
struct pixelformat_tag *format; /* abstract */
|
47
|
+
int w, h;
|
48
|
+
uint16_t pitch;
|
49
|
+
void *pixels;
|
50
|
+
} surface_header_t;
|
51
|
+
|
52
|
+
/* for Ruby/SDL 2.x, which indirectly wraps surfaces */
|
53
|
+
typedef struct rubysdl2_surface_tag {
|
54
|
+
surface_header_t *surface;
|
55
|
+
} rubysdl2_surface_t;
|
56
|
+
|
57
|
+
typedef struct vdu_wrapper_tag {
|
58
|
+
retrograph_vdu_t vdu;
|
59
|
+
VALUE surface;
|
60
|
+
retrograph_pixel_t buffer[RETROGRAPH_DISPLAY_WIDTH];
|
61
|
+
int scanline;
|
62
|
+
} vdu_wrapper_t;
|
63
|
+
|
64
|
+
#define NOT_IN_FRAME -1
|
65
|
+
|
66
|
+
static VALUE mRetrograph = Qnil;
|
67
|
+
static VALUE cVDU = Qnil;
|
68
|
+
static VALUE eRenderError = Qnil;
|
69
|
+
static VALUE mSDL = Qnil;
|
70
|
+
static VALUE cSurface = Qnil;
|
71
|
+
static VALUE CREATE_SURFACE_METHOD_NAME = Qnil;
|
72
|
+
|
73
|
+
static surface_header_t *get_rubysdl_surface_1_x(VALUE surface_r) {
|
74
|
+
surface_header_t *surface;
|
75
|
+
Data_Get_Struct(surface_r, surface_header_t, surface);
|
76
|
+
return surface;
|
77
|
+
}
|
78
|
+
|
79
|
+
static surface_header_t *get_rubysdl_surface_2_x(VALUE surface_r) {
|
80
|
+
rubysdl2_surface_t *surface;
|
81
|
+
Data_Get_Struct(surface_r, rubysdl2_surface_t, surface);
|
82
|
+
return surface->surface;
|
83
|
+
}
|
84
|
+
|
85
|
+
static surface_header_t *(*get_rubysdl_surface)(VALUE surface_r) = NULL;
|
86
|
+
|
87
|
+
static void check_errors(retrograph_vdu_t vdu) {
|
88
|
+
retrograph_error_t error;
|
89
|
+
error = retrograph_get_error(vdu);
|
90
|
+
switch (error) {
|
91
|
+
case RETROGRAPH_OK:
|
92
|
+
return;
|
93
|
+
case RETROGRAPH_NO_MEMORY:
|
94
|
+
rb_memerror();
|
95
|
+
case RETROGRAPH_INVALID_ARGUMENT:
|
96
|
+
rb_raise(rb_const_get(rb_cObject, rb_intern("ArgumentError")),
|
97
|
+
"Invalid argument");
|
98
|
+
default:
|
99
|
+
rb_raise(rb_eRuntimeError, "Unknown error");
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
static void mark_vdu(vdu_wrapper_t *vdu) {
|
104
|
+
rb_gc_mark(vdu->surface);
|
105
|
+
}
|
106
|
+
|
107
|
+
static void free_vdu(vdu_wrapper_t *vdu) {
|
108
|
+
if (vdu->vdu) {
|
109
|
+
retrograph_destroy(vdu->vdu);
|
110
|
+
}
|
111
|
+
xfree(vdu);
|
112
|
+
}
|
113
|
+
|
114
|
+
static VALUE rescue_free_vdu(VALUE data, VALUE exc) {
|
115
|
+
free_vdu((vdu_wrapper_t *)data);
|
116
|
+
rb_exc_raise(exc);
|
117
|
+
return Qnil;
|
118
|
+
}
|
119
|
+
|
120
|
+
static VALUE alloc_vdu2(VALUE data);
|
121
|
+
|
122
|
+
static VALUE alloc_vdu(VALUE klass) {
|
123
|
+
vdu_wrapper_t *vdu;
|
124
|
+
vdu = ALLOC(vdu_wrapper_t);
|
125
|
+
vdu->vdu = NULL;
|
126
|
+
vdu->surface = Qnil;
|
127
|
+
vdu->scanline = NOT_IN_FRAME;
|
128
|
+
rb_rescue2(alloc_vdu2, (VALUE)vdu,
|
129
|
+
rescue_free_vdu, (VALUE)vdu,
|
130
|
+
rb_eException, (VALUE)0);
|
131
|
+
return Data_Wrap_Struct(klass, mark_vdu, free_vdu, vdu);
|
132
|
+
}
|
133
|
+
|
134
|
+
VALUE alloc_vdu2(VALUE data) {
|
135
|
+
vdu_wrapper_t *vdu=(vdu_wrapper_t *)data;
|
136
|
+
vdu->vdu = retrograph_new();
|
137
|
+
check_errors(vdu->vdu);
|
138
|
+
return Qnil;
|
139
|
+
}
|
140
|
+
|
141
|
+
void alloc_sdl_surface(vdu_wrapper_t *vdu) {
|
142
|
+
VALUE surface;
|
143
|
+
surface = rb_funcall(cSurface, SYM2ID(CREATE_SURFACE_METHOD_NAME), 8,
|
144
|
+
INT2FIX(0), /* flags */
|
145
|
+
INT2FIX(RETROGRAPH_DISPLAY_WIDTH * UPSCALE_FACTOR),
|
146
|
+
INT2FIX(RETROGRAPH_DISPLAY_HEIGHT * UPSCALE_FACTOR),
|
147
|
+
INT2FIX(RETROGRAPH_BITS_PER_PIXEL),
|
148
|
+
INT2FIX(RETROGRAPH_RED_MASK),
|
149
|
+
INT2FIX(RETROGRAPH_GREEN_MASK),
|
150
|
+
INT2FIX(RETROGRAPH_BLUE_MASK),
|
151
|
+
INT2FIX(RETROGRAPH_ALPHA_MASK));
|
152
|
+
vdu->surface = surface;
|
153
|
+
}
|
154
|
+
|
155
|
+
static VALUE vdu_write(VALUE self, VALUE start_address_r, VALUE data_r) {
|
156
|
+
vdu_wrapper_t *vdu;
|
157
|
+
retrograph_addr_t start_address;
|
158
|
+
Check_Type(data_r, T_STRING);
|
159
|
+
Data_Get_Struct(self, vdu_wrapper_t, vdu);
|
160
|
+
start_address = (retrograph_addr_t)NUM2INT(start_address_r);
|
161
|
+
retrograph_write(vdu->vdu, start_address, RSTRING_PTR(data_r),
|
162
|
+
(size_t)RSTRING_LEN(data_r));
|
163
|
+
return Qnil;
|
164
|
+
}
|
165
|
+
|
166
|
+
static VALUE vdu_write_byte(VALUE self, VALUE start_address_r, VALUE byte_r) {
|
167
|
+
vdu_wrapper_t *vdu;
|
168
|
+
uint8_t byte;
|
169
|
+
retrograph_addr_t start_address;
|
170
|
+
Data_Get_Struct(self, vdu_wrapper_t, vdu);
|
171
|
+
start_address = (retrograph_addr_t)NUM2INT(start_address_r);
|
172
|
+
byte = (uint8_t)NUM2INT(byte_r);
|
173
|
+
retrograph_write(vdu->vdu, start_address, &byte, 1);
|
174
|
+
return Qnil;
|
175
|
+
}
|
176
|
+
|
177
|
+
static inline copy_upscale(uint8_t *output, uint8_t const * input, size_t count) {
|
178
|
+
for (; count; count--) {
|
179
|
+
UPSCALE_REPEAT(
|
180
|
+
memcpy(output, input, RETROGRAPH_BYTES_PER_PIXEL);
|
181
|
+
output += RETROGRAPH_BYTES_PER_PIXEL;
|
182
|
+
)
|
183
|
+
input += RETROGRAPH_BYTES_PER_PIXEL;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
static VALUE vdu_render_scanlines(VALUE self, VALUE n_scanlines_r) {
|
188
|
+
vdu_wrapper_t *vdu;
|
189
|
+
surface_header_t *surface;
|
190
|
+
uint8_t *current_line;
|
191
|
+
int max_scanline;
|
192
|
+
|
193
|
+
Data_Get_Struct(self, vdu_wrapper_t, vdu);
|
194
|
+
|
195
|
+
if (vdu->scanline == NOT_IN_FRAME) {
|
196
|
+
return Qnil;
|
197
|
+
}
|
198
|
+
|
199
|
+
surface = get_rubysdl_surface(vdu->surface);
|
200
|
+
|
201
|
+
max_scanline = (int)NUM2UINT(n_scanlines_r) + vdu->scanline;
|
202
|
+
if (max_scanline > RETROGRAPH_DISPLAY_HEIGHT) {
|
203
|
+
max_scanline = RETROGRAPH_DISPLAY_HEIGHT;
|
204
|
+
}
|
205
|
+
|
206
|
+
current_line = (uint8_t *)surface->pixels + vdu->scanline * surface->pitch * UPSCALE_FACTOR;
|
207
|
+
for (; vdu->scanline < max_scanline; vdu->scanline++) {
|
208
|
+
uint8_t *output;
|
209
|
+
uint8_t const *input;
|
210
|
+
retrograph_render_scanline(vdu->vdu, vdu->buffer, sizeof(vdu->buffer));
|
211
|
+
check_errors(vdu->vdu);
|
212
|
+
copy_upscale(current_line, (uint8_t const *)vdu->buffer,
|
213
|
+
RETROGRAPH_DISPLAY_WIDTH);
|
214
|
+
current_line += surface->pitch;
|
215
|
+
UPSCALE_REPEAT_MINUS1(
|
216
|
+
memcpy(current_line, current_line - surface->pitch,
|
217
|
+
RETROGRAPH_DISPLAY_WIDTH * RETROGRAPH_BYTES_PER_PIXEL *
|
218
|
+
UPSCALE_FACTOR);
|
219
|
+
current_line += surface->pitch;
|
220
|
+
)
|
221
|
+
}
|
222
|
+
|
223
|
+
return Qnil;
|
224
|
+
}
|
225
|
+
|
226
|
+
static VALUE vdu_render_frame_inner(VALUE self) {
|
227
|
+
vdu_wrapper_t *vdu;
|
228
|
+
Data_Get_Struct(self, vdu_wrapper_t, vdu);
|
229
|
+
retrograph_begin_frame(vdu->vdu);
|
230
|
+
check_errors(vdu->vdu);
|
231
|
+
vdu->scanline = 0;
|
232
|
+
if (rb_block_given_p()) {
|
233
|
+
rb_yield(Qundef);
|
234
|
+
}
|
235
|
+
vdu_render_scanlines(self, INT2FIX(RETROGRAPH_DISPLAY_HEIGHT));
|
236
|
+
return Qnil;
|
237
|
+
}
|
238
|
+
|
239
|
+
static VALUE vdu_render_frame_cleanup(VALUE self) {
|
240
|
+
vdu_wrapper_t *vdu;
|
241
|
+
Data_Get_Struct(self, vdu_wrapper_t, vdu);
|
242
|
+
vdu->scanline = NOT_IN_FRAME;
|
243
|
+
return Qnil;
|
244
|
+
}
|
245
|
+
|
246
|
+
static VALUE vdu_render_frame(VALUE self) {
|
247
|
+
vdu_wrapper_t *vdu;
|
248
|
+
Data_Get_Struct(self, vdu_wrapper_t, vdu);
|
249
|
+
if (vdu->scanline != NOT_IN_FRAME) {
|
250
|
+
rb_raise(eRenderError, "Already in frame");
|
251
|
+
}
|
252
|
+
if (vdu->surface == Qnil) {
|
253
|
+
alloc_sdl_surface(vdu);
|
254
|
+
}
|
255
|
+
rb_ensure(vdu_render_frame_inner, self, vdu_render_frame_cleanup, self);
|
256
|
+
return vdu->surface;
|
257
|
+
}
|
258
|
+
|
259
|
+
static VALUE init_sdl(VALUE self) {
|
260
|
+
VALUE rubysdl_version;
|
261
|
+
mSDL = rb_const_get(rb_cObject, rb_intern("SDL"));
|
262
|
+
|
263
|
+
rubysdl_version = rb_const_get(mSDL, rb_intern("VERSION"));
|
264
|
+
if (!strncmp(StringValueCStr(rubysdl_version), "1.", 2)) {
|
265
|
+
CREATE_SURFACE_METHOD_NAME = ID2SYM(rb_intern("new"));
|
266
|
+
get_rubysdl_surface = get_rubysdl_surface_1_x;
|
267
|
+
} else if (!strncmp(StringValueCStr(rubysdl_version), "2.", 2)) {
|
268
|
+
CREATE_SURFACE_METHOD_NAME = ID2SYM(rb_intern("createWithFormat"));
|
269
|
+
get_rubysdl_surface = get_rubysdl_surface_2_x;
|
270
|
+
} else {
|
271
|
+
rb_raise(rb_eLoadError, "Unrecognized Ruby/SDL version");
|
272
|
+
}
|
273
|
+
|
274
|
+
cSurface = rb_const_get(mSDL, rb_intern("Surface"));
|
275
|
+
rb_define_method(cVDU, "render_frame_sdl", vdu_render_frame, 0);
|
276
|
+
}
|
277
|
+
|
278
|
+
void Init_retrograph(void) {
|
279
|
+
rb_global_variable(&mSDL);
|
280
|
+
rb_global_variable(&cSurface);
|
281
|
+
rb_global_variable(&CREATE_SURFACE_METHOD_NAME);
|
282
|
+
|
283
|
+
rb_global_variable(&mRetrograph);
|
284
|
+
mRetrograph = rb_define_module("Retrograph");
|
285
|
+
|
286
|
+
#define DEFINE_CONSTANT(base_name) \
|
287
|
+
rb_const_set(mRetrograph, rb_intern(#base_name), \
|
288
|
+
INT2FIX(RETROGRAPH_##base_name))
|
289
|
+
|
290
|
+
DEFINE_CONSTANT(DISPLAY_WIDTH);
|
291
|
+
DEFINE_CONSTANT(DISPLAY_HEIGHT);
|
292
|
+
DEFINE_CONSTANT(BYTES_PER_PIXEL);
|
293
|
+
DEFINE_CONSTANT(BITS_PER_PIXEL);
|
294
|
+
|
295
|
+
DEFINE_CONSTANT(RED_SHIFT);
|
296
|
+
DEFINE_CONSTANT(GREEN_SHIFT);
|
297
|
+
DEFINE_CONSTANT(BLUE_SHIFT);
|
298
|
+
|
299
|
+
DEFINE_CONSTANT(RED_MASK);
|
300
|
+
DEFINE_CONSTANT(GREEN_MASK);
|
301
|
+
DEFINE_CONSTANT(BLUE_MASK);
|
302
|
+
DEFINE_CONSTANT(ALPHA_MASK);
|
303
|
+
|
304
|
+
#undef DEFINE_CONSTANT
|
305
|
+
|
306
|
+
rb_const_set(mRetrograph, rb_intern("OUTPUT_WIDTH"),
|
307
|
+
INT2FIX(RETROGRAPH_DISPLAY_WIDTH * UPSCALE_FACTOR));
|
308
|
+
rb_const_set(mRetrograph, rb_intern("OUTPUT_HEIGHT"),
|
309
|
+
INT2FIX(RETROGRAPH_DISPLAY_HEIGHT * UPSCALE_FACTOR));
|
310
|
+
|
311
|
+
rb_global_variable(&cVDU);
|
312
|
+
cVDU = rb_define_class_under(mRetrograph, "VDU", rb_cObject);
|
313
|
+
|
314
|
+
rb_global_variable(&eRenderError);
|
315
|
+
eRenderError = rb_define_class_under(mRetrograph, "RenderError",
|
316
|
+
rb_eRuntimeError);
|
317
|
+
|
318
|
+
rb_define_alloc_func(cVDU, alloc_vdu);
|
319
|
+
rb_define_method(cVDU, "write", vdu_write, 2);
|
320
|
+
rb_define_method(cVDU, "write_byte", vdu_write_byte, 2);
|
321
|
+
rb_define_method(cVDU, "wait_scanlines", vdu_render_scanlines, 1);
|
322
|
+
|
323
|
+
rb_define_singleton_method(mRetrograph, "_init_sdl", init_sdl, 0);
|
324
|
+
}
|