retrograph 0.2.1 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
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 CHANGED
@@ -6,19 +6,18 @@ console. It is similar in capability to the Sega Master
6
6
  System's VDP, with some additional features and direct
7
7
  support for text modes.
8
8
 
9
- Retrograph requires Ruby/SDL and outputs to an SDL
10
- surface.
9
+ Retrograph currently supports Ruby/SDL for output; other
10
+ output targets may be added in the future.
11
11
 
12
12
  == Feature Overview ==
13
13
 
14
14
  * 256x244 pixel display
15
15
  * 16k VRAM
16
- * 40x25 and 32x28 text modes
17
- * 2bpp and 4bpp graphics modes
18
16
  * 64 colors total
19
- * 31 simultaneous colors (16 background, 15 sprite)
20
- * 32x32 background layer
21
- * 64 sprites, max 8 per scanline
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
22
21
 
23
22
  == Usage ==
24
23
 
@@ -27,65 +26,54 @@ represented by an instance of the Retrograph::VDU class.
27
26
  You can interact with the VDU by writing byte strings
28
27
  into its memory using Retrograph::VDU#write, which takes
29
28
  a start address (in the VDU's 16-bit address space) and
30
- a string to write.
29
+ a string to write. The method Retrograph::VDU#write_byte
30
+ is also provided for writing individual bytes.
31
31
 
32
- To render a frame of video, use
33
- Retrograph::VDU#render_frame. The SDL surface which
34
- serves as an output buffer is available via
35
- Retrograph::VDU#surface.
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.
36
40
 
37
41
  Scanline-based effects are possible if you provide a block
38
- to Retrograph::VDU#render_frame and call
39
- Retrograph::VDU#render_scanlines to render scanlines in
40
- between the execution of Ruby code. For example, if we
41
- wanted palette entry 0 to be blue within the top half of
42
- the display and red within the bottom half of the display,
43
- we could write:
44
-
45
- vdu.render_frame do
46
- vdu.write(0x7f00, "\x03")
47
- vdu.render_scanlines(Retrograph::DISPLAY_HEIGHT/2)
48
- vdu.write(0x7f00, "\x30")
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)
49
59
  end
50
60
 
51
- The integer passed to Retrograph::VDU#render_scanlines is
52
- an integer count of scanlines to render; each call renders
53
- an additional number of scanlines. Any remaining scanlines
54
- not rendered by calls to Retrograph::VDU#render_scanlines
55
- are rendered by Retrograph::VDU#render_frame when the block
56
- completes.
57
-
58
- See the end of this document for a map of registers and
59
- memory locations within the VDU's address space.
60
-
61
61
  == Important Notes ==
62
62
 
63
- Both background (and sprites, in graphics modes) must be
64
- explicitly enabled by setting bits 2 (and 3) of register
65
- $7FF8 or else they will not be displayed.
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.
66
67
 
67
- Patterns and name tables may overlap in memory (they must
68
- do so in graphics modes); typically you cannot have
69
- simultaneously valid pattern and name data at the same
70
- location, so in those cases you will need to sacrifice
71
- particular name or pattern entries.
72
-
73
- Unlike most authentic 8-bit systems, any register or memory
74
- location may be changed in between scanlines, including the
75
- vertical scroll register and even the video mode. This
76
- permits fairly powerful split-screen effects, and in some
77
- cases may be used to exceed simultaneous color, sprite, or
78
- pattern limitations.
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.
79
73
 
80
74
  In text modes, you will need to load your own font into the
81
75
  pattern table; the VDU does not provide one by default.
82
76
 
83
- Also unlike most authentic 8-bit systems, Retrograph uses a
84
- packed rather than a planar representation for pattern
85
- data. Additionally, within each byte of pattern data, the
86
- leftmost pixels are in the least significant position,
87
- which may be backwards from what you expect.
88
-
89
77
  Sprites patterns are always 4bpp, even in the 2bpp mode.
90
78
  In Graphics A, one half of VRAM is occupied by 512 2bpp
91
79
  patterns (for the background), and the other half by 256
@@ -94,7 +82,28 @@ of VRAM is occupied by just 512 4bpp patterns, with the
94
82
  upper half of those available to sprites.
95
83
 
96
84
  When scrolling horizontally, the leftmost background
97
- column will not be displayed if it is partway offscreen.
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.
98
107
 
99
108
  == VDU Memory Map ==
100
109
 
@@ -117,19 +126,17 @@ $7FF8 - $7FFF: VDU Registers (8 bytes)
117
126
  $7FF9: Pattern Table Base
118
127
  Text A + B:
119
128
  addr = (base & 0b00110000) << 8
120
- base = $00, $10, $20, or $30
121
- addr = $0000, $1000, $2000, or
122
- $3000
129
+ addr may be one of:
130
+ $0000, $1000, $2000, or $3000
123
131
  Graphics A + B:
124
132
  addr = (base & 0b00100000) << 8
125
- base = $00 or $20
126
- addr = $0000 or $2000
133
+ addr may be one of:
134
+ $0000 or $2000
127
135
  $7FFA: Name Table Base
128
136
  addr = ((base & 0b00110000) |
129
137
  0b00001000) << 8
130
- base = $00, $10, $20, or $30
131
- addr = $0800, $1800, $2800, or
132
- $3800
138
+ addr may be one of:
139
+ $0800, $1800, $2800, or $3800
133
140
  $7FFB: unused
134
141
  $7FFC: BG scroll X*
135
142
  $7FFD: BG scroll Y**
@@ -149,7 +156,16 @@ register $7FF9.
149
156
 
150
157
  The rows constituting each pattern in the table are given
151
158
  in sequence from top to bottom. Within each byte, the
152
- leftmost pixel is in the least significant position.
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:
153
169
 
154
170
  Text A: 256 patterns * 8 bytes per pattern = 2k
155
171
  6x8 pixel patterns
@@ -209,9 +225,9 @@ regardless of the high color bits.)
209
225
 
210
226
  === Sprite Table ===
211
227
 
212
- Sprite patterns start 2k bytes after the pattern table base
213
- address specified in register $7FF9. The sprite table
214
- itself always begins at $7E00.
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.
215
231
 
216
232
  4 bytes * 64 sprites = 256 bytes
217
233
 
data/Rakefile CHANGED
@@ -1,54 +1,71 @@
1
1
  require 'rake'
2
2
  require 'rake/gempackagetask'
3
- require 'spec/rake/spectask'
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
4
17
  require 'rake/clean'
5
18
 
6
- GEM_VERSION = '0.2.1'
19
+ GEM_VERSION = '0.5'
7
20
 
8
- CLEAN.include("**/*.o")
9
- CLEAN.include("**/*.so")
10
21
  CLEAN.include("spec/**/*-output.bmp")
11
- CLOBBER.include("ext/Makefile")
22
+ CLOBBER.include("lib/1.8")
23
+ CLOBBER.include("lib/1.9")
12
24
 
13
25
  task :clobber => [:clean]
14
26
 
15
- Spec::Rake::SpecTask.new do |task|
16
- task.ruby_opts << '-rrubygems'
17
- task.libs << 'ext'
18
- task.spec_files = FileList["spec/**/*_spec.rb"]
19
- end
20
-
21
- task :build do
22
- sh "cd ext && #{Gem.ruby} extconf.rb && make"
23
- end
24
-
25
27
  gemspec = Gem::Specification.new do |gemspec|
26
28
  gemspec.name = "retrograph"
27
29
  gemspec.version = GEM_VERSION
28
30
  gemspec.author = "MenTaLguY <mental@rydia.net>"
29
31
  gemspec.summary = "Retrograph, a retrodisplay."
30
32
  gemspec.description = <<EOS
31
- Retrograph is a software scanline renderer which simulates a late-era 8-bit display.
32
- Its graphical capabilities are similar to those of the Sega Master System.
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.
33
36
  EOS
34
37
  gemspec.homepage = "http://rubyforge.org/projects/retrograph"
35
38
  gemspec.email = "mental@rydia.net"
36
39
  gemspec.rubyforge_project = 'retrograph'
37
- gemspec.files = FileList['Rakefile', 'README',
38
- 'src/retrograph.h', 'src/retrograph.c',
39
- 'ext/extconf.rb', 'ext/retrograph.c',
40
+ gemspec.files = FileList['Rakefile', 'README', 'COPYING',
41
+ 'src/**/*.h', 'src/**/*.c', 'lib/**/*.rb',
42
+ 'ext/**/extconf.rb', 'ext/**/*.c', 'ext/**/*.h',
40
43
  'spec/**/*_spec.rb', 'spec/**/*.case',
41
44
  'spec/**/*.png']
42
- gemspec.extensions = ['ext/extconf.rb']
43
- gemspec.require_paths = ['ext']
45
+ gemspec.extensions = FileList['ext/**/extconf.rb']
46
+ gemspec.require_paths = ['lib']
44
47
  gemspec.platform = Gem::Platform::RUBY
45
- gemspec.add_dependency("rubysdl", ">= 1.3.0")
46
48
  end
47
49
 
48
50
  Rake::GemPackageTask.new(gemspec) do |pkg|
49
51
  pkg.need_tar = true
50
52
  end
51
53
 
52
- task :spec => [:build]
53
- task :package => [:clean, :spec]
54
- task :default => [:clean, :spec]
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,6 @@
1
+ require 'mkmf'
2
+
3
+ if RUBY_VERSION =~ /^1\.8\./
4
+ $CPPFLAGS += ' -DRUBY_1_8'
5
+ end
6
+ create_makefile('retrograph')
@@ -21,11 +21,24 @@
21
21
  * THE SOFTWARE.
22
22
  */
23
23
  #include <ruby.h>
24
+ #ifdef RUBY_1_8
24
25
  #include <intern.h>
26
+ #else
27
+ #include <ruby/intern.h>
28
+ #endif
25
29
  #include <string.h>
26
30
 
27
- #include "../src/retrograph.h"
28
- #include "../src/retrograph.c"
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;
29
42
 
30
43
  /* First few fields of SDL_Surface */
31
44
  typedef struct surface_header_tag {
@@ -44,6 +57,7 @@ typedef struct rubysdl2_surface_tag {
44
57
  typedef struct vdu_wrapper_tag {
45
58
  retrograph_vdu_t vdu;
46
59
  VALUE surface;
60
+ retrograph_pixel_t buffer[RETROGRAPH_DISPLAY_WIDTH];
47
61
  int scanline;
48
62
  } vdu_wrapper_t;
49
63
 
@@ -107,7 +121,6 @@ static VALUE alloc_vdu2(VALUE data);
107
121
 
108
122
  static VALUE alloc_vdu(VALUE klass) {
109
123
  vdu_wrapper_t *vdu;
110
- VALUE vdu_r;
111
124
  vdu = ALLOC(vdu_wrapper_t);
112
125
  vdu->vdu = NULL;
113
126
  vdu->surface = Qnil;
@@ -120,20 +133,23 @@ static VALUE alloc_vdu(VALUE klass) {
120
133
 
121
134
  VALUE alloc_vdu2(VALUE data) {
122
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) {
123
142
  VALUE surface;
124
143
  surface = rb_funcall(cSurface, SYM2ID(CREATE_SURFACE_METHOD_NAME), 8,
125
144
  INT2FIX(0), /* flags */
126
- INT2FIX(RETROGRAPH_DISPLAY_WIDTH),
127
- INT2FIX(RETROGRAPH_DISPLAY_HEIGHT),
145
+ INT2FIX(RETROGRAPH_DISPLAY_WIDTH * UPSCALE_FACTOR),
146
+ INT2FIX(RETROGRAPH_DISPLAY_HEIGHT * UPSCALE_FACTOR),
128
147
  INT2FIX(RETROGRAPH_BITS_PER_PIXEL),
129
148
  INT2FIX(RETROGRAPH_RED_MASK),
130
149
  INT2FIX(RETROGRAPH_GREEN_MASK),
131
150
  INT2FIX(RETROGRAPH_BLUE_MASK),
132
151
  INT2FIX(RETROGRAPH_ALPHA_MASK));
133
152
  vdu->surface = surface;
134
- vdu->vdu = retrograph_new();
135
- check_errors(vdu->vdu);
136
- return Qnil;
137
153
  }
138
154
 
139
155
  static VALUE vdu_write(VALUE self, VALUE start_address_r, VALUE data_r) {
@@ -142,15 +158,30 @@ static VALUE vdu_write(VALUE self, VALUE start_address_r, VALUE data_r) {
142
158
  Check_Type(data_r, T_STRING);
143
159
  Data_Get_Struct(self, vdu_wrapper_t, vdu);
144
160
  start_address = (retrograph_addr_t)NUM2INT(start_address_r);
145
- retrograph_write(vdu->vdu, start_address, RSTRING(data_r)->ptr,
146
- (size_t)RSTRING(data_r)->len);
161
+ retrograph_write(vdu->vdu, start_address, RSTRING_PTR(data_r),
162
+ (size_t)RSTRING_LEN(data_r));
147
163
  return Qnil;
148
164
  }
149
165
 
150
- static VALUE vdu_get_surface(VALUE self) {
166
+ static VALUE vdu_write_byte(VALUE self, VALUE start_address_r, VALUE byte_r) {
151
167
  vdu_wrapper_t *vdu;
168
+ uint8_t byte;
169
+ retrograph_addr_t start_address;
152
170
  Data_Get_Struct(self, vdu_wrapper_t, vdu);
153
- return vdu->surface;
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
+ }
154
185
  }
155
186
 
156
187
  static VALUE vdu_render_scanlines(VALUE self, VALUE n_scanlines_r) {
@@ -162,7 +193,7 @@ static VALUE vdu_render_scanlines(VALUE self, VALUE n_scanlines_r) {
162
193
  Data_Get_Struct(self, vdu_wrapper_t, vdu);
163
194
 
164
195
  if (vdu->scanline == NOT_IN_FRAME) {
165
- rb_raise(eRenderError, "Not in frame");
196
+ return Qnil;
166
197
  }
167
198
 
168
199
  surface = get_rubysdl_surface(vdu->surface);
@@ -172,13 +203,21 @@ static VALUE vdu_render_scanlines(VALUE self, VALUE n_scanlines_r) {
172
203
  max_scanline = RETROGRAPH_DISPLAY_HEIGHT;
173
204
  }
174
205
 
175
- current_line = (uint8_t *)surface->pixels + vdu->scanline * surface->pitch;
206
+ current_line = (uint8_t *)surface->pixels + vdu->scanline * surface->pitch * UPSCALE_FACTOR;
176
207
  for (; vdu->scanline < max_scanline; vdu->scanline++) {
177
- retrograph_render_scanline(vdu->vdu, current_line,
178
- RETROGRAPH_DISPLAY_WIDTH *
179
- RETROGRAPH_BYTES_PER_PIXEL);
208
+ uint8_t *output;
209
+ uint8_t const *input;
210
+ retrograph_render_scanline(vdu->vdu, vdu->buffer, sizeof(vdu->buffer));
180
211
  check_errors(vdu->vdu);
212
+ copy_upscale(current_line, (uint8_t const *)vdu->buffer,
213
+ RETROGRAPH_DISPLAY_WIDTH);
181
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
+ )
182
221
  }
183
222
 
184
223
  return Qnil;
@@ -206,27 +245,22 @@ static VALUE vdu_render_frame_cleanup(VALUE self) {
206
245
 
207
246
  static VALUE vdu_render_frame(VALUE self) {
208
247
  vdu_wrapper_t *vdu;
209
- int block_given;
210
248
  Data_Get_Struct(self, vdu_wrapper_t, vdu);
211
- block_given = rb_block_given_p();
212
249
  if (vdu->scanline != NOT_IN_FRAME) {
213
250
  rb_raise(eRenderError, "Already in frame");
214
251
  }
252
+ if (vdu->surface == Qnil) {
253
+ alloc_sdl_surface(vdu);
254
+ }
215
255
  rb_ensure(vdu_render_frame_inner, self, vdu_render_frame_cleanup, self);
216
256
  return vdu->surface;
217
257
  }
218
258
 
219
- void Init_retrograph(void) {
259
+ static VALUE init_sdl(VALUE self) {
220
260
  VALUE rubysdl_version;
221
- rb_require("sdl");
222
-
223
- rb_global_variable(&mSDL);
224
261
  mSDL = rb_const_get(rb_cObject, rb_intern("SDL"));
225
- rb_global_variable(&cSurface);
226
- cSurface = rb_const_get(mSDL, rb_intern("Surface"));
227
262
 
228
263
  rubysdl_version = rb_const_get(mSDL, rb_intern("VERSION"));
229
- rb_global_variable(&CREATE_SURFACE_METHOD_NAME);
230
264
  if (!strncmp(StringValueCStr(rubysdl_version), "1.", 2)) {
231
265
  CREATE_SURFACE_METHOD_NAME = ID2SYM(rb_intern("new"));
232
266
  get_rubysdl_surface = get_rubysdl_surface_1_x;
@@ -237,6 +271,15 @@ void Init_retrograph(void) {
237
271
  rb_raise(rb_eLoadError, "Unrecognized Ruby/SDL version");
238
272
  }
239
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
+
240
283
  rb_global_variable(&mRetrograph);
241
284
  mRetrograph = rb_define_module("Retrograph");
242
285
 
@@ -260,6 +303,11 @@ void Init_retrograph(void) {
260
303
 
261
304
  #undef DEFINE_CONSTANT
262
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
+
263
311
  rb_global_variable(&cVDU);
264
312
  cVDU = rb_define_class_under(mRetrograph, "VDU", rb_cObject);
265
313
 
@@ -269,7 +317,8 @@ void Init_retrograph(void) {
269
317
 
270
318
  rb_define_alloc_func(cVDU, alloc_vdu);
271
319
  rb_define_method(cVDU, "write", vdu_write, 2);
272
- rb_define_method(cVDU, "surface", vdu_get_surface, 0);
273
- rb_define_method(cVDU, "render_frame", vdu_render_frame, 0);
274
- rb_define_method(cVDU, "render_scanlines", vdu_render_scanlines, 1);
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);
275
324
  }
@@ -0,0 +1,26 @@
1
+ # retrograph/sdl
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
+ require 'retrograph'
24
+ require 'sdl'
25
+
26
+ Retrograph._init_sdl
data/lib/retrograph.rb ADDED
@@ -0,0 +1,29 @@
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
+
24
+ # augment search path for fat binary gems
25
+ ruby_version = /^\d+\.\d+/.match(RUBY_VERSION)[0]
26
+ base_dir = File.dirname(__FILE__)
27
+ $:.push File.join(base_dir, ruby_version)
28
+
29
+ require 'retrograph.so'
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,10 +1,11 @@
1
- require 'sdl'
2
- require 'retrograph'
1
+ require 'retrograph/sdl'
3
2
 
4
3
  describe Retrograph do
5
4
  constants = [
6
5
  [:DISPLAY_WIDTH, 256],
7
6
  [:DISPLAY_HEIGHT, 224],
7
+ [:OUTPUT_WIDTH, 512],
8
+ [:OUTPUT_HEIGHT, 448],
8
9
  [:BYTES_PER_PIXEL, 4],
9
10
  [:BITS_PER_PIXEL, 32],
10
11
  [:RED_SHIFT, 16],
@@ -69,16 +70,16 @@ module ImageTests
69
70
  for offset, data in writes
70
71
  vdu.write(offset, data)
71
72
  end
72
- output_image = vdu.render_frame
73
+ output_image = vdu.render_frame_sdl
73
74
  output_file = image_file.sub(/\.[^.]*$/, '-output.bmp')
74
75
  output_image.save_bmp(output_file)
75
76
 
76
77
  raw_image = SDL::Surface.load(image_file)
77
78
  raw_image.set_alpha(0, SDL::ALPHA_OPAQUE)
78
79
  raw_image.set_color_key(0, 0)
79
- reference_image = SDL::Surface.new(0, 256, 224, 32,
80
+ reference_image = SDL::Surface.new(0, raw_image.w, raw_image.h, 32,
80
81
  RMASK, GMASK, BMASK, AMASK)
81
- SDL::Surface.blit(raw_image, 0, 0, 256, 224, reference_image, 0, 0)
82
+ SDL::Surface.blit(raw_image, 0, 0, 0, 0, reference_image, 0, 0)
82
83
  raw_image = nil
83
84
  if output_image.pixels != reference_image.pixels
84
85
  raise "Output does not match."
@@ -92,12 +93,12 @@ describe Retrograph::VDU do
92
93
  @vdu = Retrograph::VDU.new
93
94
  end
94
95
 
95
- it "should have a surface with the right characteristics" do
96
- surface = @vdu.surface
96
+ it "should render to a surface with the right characteristics" do
97
+ surface = @vdu.render_frame_sdl
97
98
  surface.should be_an_instance_of(SDL::Surface)
98
99
  surface.flags.should == 0
99
- surface.w.should == Retrograph::DISPLAY_WIDTH
100
- surface.h.should == Retrograph::DISPLAY_HEIGHT
100
+ surface.w.should == Retrograph::OUTPUT_WIDTH
101
+ surface.h.should == Retrograph::OUTPUT_HEIGHT
101
102
  surface.bpp.should == Retrograph::BITS_PER_PIXEL
102
103
  surface.Rmask.should == Retrograph::RED_MASK
103
104
  surface.Gmask.should == Retrograph::GREEN_MASK
@@ -105,29 +106,29 @@ describe Retrograph::VDU do
105
106
  surface.Amask.should == Retrograph::ALPHA_MASK
106
107
  end
107
108
 
108
- it "should return that surface each time from #render_frame" do
109
- output = @vdu.render_frame
110
- output.should == @vdu.surface
109
+ it "should have a write method which takes an offset and a byte string" do
110
+ @vdu.write(0x0001, "foobar")
111
111
  end
112
112
 
113
- it "should not allow nested calls to #render_frame" do
113
+ it "should have a write_bytes method which takes an offset and a byte" do
114
+ @vdu.write_byte(0x0001, 42)
115
+ end
116
+
117
+ it "should not allow nested calls to #render_frame_sdl" do
114
118
  lambda {
115
- @vdu.render_frame { @vdu.render_frame }
119
+ @vdu.render_frame_sdl { @vdu.render_frame_sdl }
116
120
  }.should raise_error(Retrograph::RenderError, "Already in frame")
117
121
  end
118
122
 
119
- it "should not allow naked calls to #render_scanlines" do
120
- lambda {
121
- @vdu.render_scanlines(30)
122
- }.should raise_error(Retrograph::RenderError, "Not in frame")
123
+ it "should allow naked calls to #wait_scanlines (as a noop)" do
124
+ @vdu.wait_scanlines(30)
123
125
  end
124
126
 
125
- it "should allow calls to #render_scanlines within a frame" do
126
- @vdu.render_frame { @vdu.render_scanlines(30) }
127
+ it "should allow calls to #wait_scanlines within a frame" do
128
+ @vdu.render_frame_sdl { @vdu.wait_scanlines(30) }
127
129
  end
128
130
 
129
131
  extend ImageTests
130
- spec_dir = File.join(File.split(__FILE__).first,
131
- 'image-specs')
132
+ spec_dir = File.join(File.split(__FILE__).first, 'image-specs')
132
133
  build_image_specs(spec_dir)
133
134
  end
data/src/retrograph.c CHANGED
@@ -91,9 +91,9 @@ struct retrograph_vdu_tag {
91
91
  uint8_t bg_scroll_y;
92
92
 
93
93
  unsigned current_scanline;
94
- uint32_t palette_cache[32];
94
+ retrograph_pixel_t palette_cache[32];
95
95
  /* +16 to accomodate double-width sprites */
96
- uint32_t row_buffer[RETROGRAPH_DISPLAY_WIDTH + 16];
96
+ retrograph_pixel_t row_buffer[RETROGRAPH_DISPLAY_WIDTH + 16];
97
97
  };
98
98
 
99
99
  static retrograph_error_t no_memory_error = RETROGRAPH_NO_MEMORY;
@@ -283,7 +283,9 @@ static void update_palette_cache(retrograph_vdu_t vdu) {
283
283
  }
284
284
  }
285
285
 
286
- static void clear_row_buffer(uint32_t * const out_buffer, uint32_t const color) {
286
+ static void clear_row_buffer(retrograph_pixel_t * const out_buffer,
287
+ retrograph_pixel_t const color)
288
+ {
287
289
  int i;
288
290
  for (i = 0; i < RETROGRAPH_DISPLAY_WIDTH; i++) {
289
291
  out_buffer[i] = color;
@@ -291,14 +293,14 @@ static void clear_row_buffer(uint32_t * const out_buffer, uint32_t const color)
291
293
  }
292
294
 
293
295
  #define DEFINE_RENDER_CHAR(width) \
294
- static inline void render_char ## width(uint32_t * const out_buffer, \
296
+ static inline void render_char ## width(retrograph_pixel_t * const out_buffer, \
295
297
  uint8_t const c, \
296
298
  uint8_t const attrs, \
297
299
  uint8_t const * const patterns, \
298
- uint32_t const * const palette) \
300
+ retrograph_pixel_t const * const palette) \
299
301
  { \
300
302
  uint8_t pattern_row; \
301
- uint32_t colors[2]; \
303
+ retrograph_pixel_t colors[2]; \
302
304
  int i; \
303
305
  colors[1] = palette[attrs % 16]; \
304
306
  colors[0] = palette[attrs / 16]; \
@@ -312,9 +314,9 @@ static inline void render_char ## width(uint32_t * const out_buffer, \
312
314
  DEFINE_RENDER_CHAR(6)
313
315
  DEFINE_RENDER_CHAR(8)
314
316
 
315
- static void render_line_text_a(uint32_t *out_buffer, uint8_t const *names,
317
+ static void render_line_text_a(retrograph_pixel_t *out_buffer, uint8_t const *names,
316
318
  uint8_t y_fine, uint8_t const *patterns,
317
- uint32_t const *palette, char enable_bg)
319
+ retrograph_pixel_t const *palette, char enable_bg)
318
320
  {
319
321
  int i;
320
322
 
@@ -333,9 +335,9 @@ static void render_line_text_a(uint32_t *out_buffer, uint8_t const *names,
333
335
  }
334
336
  }
335
337
 
336
- static void render_line_text_b(uint32_t *out_buffer, uint8_t const *names,
338
+ static void render_line_text_b(retrograph_pixel_t *out_buffer, uint8_t const *names,
337
339
  uint8_t y_fine, uint8_t const *patterns,
338
- uint32_t const *palette, char enable_bg)
340
+ retrograph_pixel_t const *palette, char enable_bg)
339
341
  {
340
342
  int i;
341
343
  if (enable_bg) {
@@ -361,10 +363,10 @@ static inline uint32_t unpack_uint32(uint8_t const * const bytes) {
361
363
  out_buffer[i*2+1] = palette[color | base_color]
362
364
 
363
365
  #define DEFINE_RENDER_TILE(bpp, suffix, RENDER_PIXEL, row_type, unpack_row) \
364
- static inline void render_ ## suffix ## bpp(uint32_t *out_buffer, \
366
+ static inline void render_ ## suffix ## bpp(retrograph_pixel_t *out_buffer, \
365
367
  uint16_t cell, \
366
368
  uint8_t const *vflip_patterns[2], \
367
- uint32_t const *palette) \
369
+ retrograph_pixel_t const *palette) \
368
370
  { \
369
371
  row_type pattern_row; \
370
372
  uint8_t base_color; \
@@ -405,12 +407,12 @@ DEFINE_RENDER_TILE(4, tile, RENDER_SINGLE_PIXEL, uint32_t, unpack_uint32)
405
407
  DEFINE_RENDER_TILE(4, tile_double, RENDER_DOUBLE_PIXEL, uint32_t, unpack_uint32)
406
408
 
407
409
  #define DEFINE_RENDER_BG_LINE(bpp) \
408
- static void render_bg_line ## bpp(uint32_t *out_buffer, \
410
+ static void render_bg_line ## bpp(retrograph_pixel_t *out_buffer, \
409
411
  uint8_t x_coarse, uint8_t x_fine, \
410
412
  uint8_t const *names, \
411
413
  uint8_t const *vflip_patterns[2], \
412
414
  uint16_t toggle_bits, \
413
- uint32_t const *palette) \
415
+ retrograph_pixel_t const *palette) \
414
416
  { \
415
417
  int i; \
416
418
  uint8_t const *name_cursor; \
@@ -457,10 +459,10 @@ static inline unsigned compute_sprite_line(uint8_t row, uint8_t const *sprite) {
457
459
  return sprite_line;
458
460
  }
459
461
 
460
- static void render_sprites_line(uint32_t *out_buffer, uint8_t row,
462
+ static void render_sprites_line(retrograph_pixel_t *out_buffer, uint8_t row,
461
463
  uint8_t const *sprites,
462
464
  uint8_t const *patterns,
463
- uint32_t const *palette)
465
+ retrograph_pixel_t const *palette)
464
466
  {
465
467
  unsigned sprite_lines[MAX_SPRITES];
466
468
  int i, n_sprites;
@@ -509,14 +511,14 @@ static void render_sprites_line(uint32_t *out_buffer, uint8_t row,
509
511
  }
510
512
  }
511
513
 
512
- static void render_line_graphics_a(uint32_t *out_buffer,
514
+ static void render_line_graphics_a(retrograph_pixel_t *out_buffer,
513
515
  uint8_t const * const names,
514
516
  uint8_t x_coarse, uint8_t x_fine,
515
517
  uint8_t y_fine, uint8_t row,
516
518
  uint8_t const *patterns,
517
519
  retrograph_addr_t pattern_base,
518
520
  uint8_t const *sprites,
519
- uint32_t const *palette,
521
+ retrograph_pixel_t const *palette,
520
522
  char enable_bg, char enable_sprites)
521
523
  {
522
524
  uint8_t const *vflip_patterns[2];
@@ -540,13 +542,13 @@ static void render_line_graphics_a(uint32_t *out_buffer,
540
542
  }
541
543
  }
542
544
 
543
- static void render_line_graphics_b(uint32_t *out_buffer, uint8_t const *names,
545
+ static void render_line_graphics_b(retrograph_pixel_t *out_buffer, uint8_t const *names,
544
546
  uint8_t x_coarse, uint8_t x_fine,
545
547
  uint8_t y_fine, uint8_t row,
546
548
  uint8_t const *patterns,
547
549
  retrograph_addr_t pattern_base,
548
550
  uint8_t const *sprites,
549
- uint32_t const *palette,
551
+ retrograph_pixel_t const *palette,
550
552
  char enable_bg, char enable_sprites)
551
553
  {
552
554
  uint8_t const *vflip_patterns[2];
data/src/retrograph.h CHANGED
@@ -29,7 +29,8 @@
29
29
  #define RETROGRAPH_DISPLAY_WIDTH 256
30
30
  #define RETROGRAPH_DISPLAY_HEIGHT 224
31
31
 
32
- #define RETROGRAPH_BYTES_PER_PIXEL 4
32
+ typedef uint32_t retrograph_pixel_t;
33
+ #define RETROGRAPH_BYTES_PER_PIXEL sizeof(retrograph_pixel_t)
33
34
  #define RETROGRAPH_BITS_PER_PIXEL (RETROGRAPH_BYTES_PER_PIXEL * 8)
34
35
 
35
36
  #define RETROGRAPH_RED_SHIFT 16
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: retrograph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: "0.5"
5
5
  platform: ruby
6
6
  authors:
7
7
  - MenTaLguY <mental@rydia.net>
@@ -9,37 +9,32 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-22 00:00:00 -04:00
12
+ date: 2009-07-24 00:00:00 -04:00
13
13
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: rubysdl
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 1.3.0
24
- version:
14
+ dependencies: []
15
+
25
16
  description: |
26
- Retrograph is a software scanline renderer which simulates a late-era 8-bit display.
27
- Its graphical capabilities are similar to those of the Sega Master System.
17
+ Retrograph is a software scanline renderer which simulates a late-era 8-bit
18
+ display. Its graphical capabilities are similar to those of the Sega Master
19
+ System.
28
20
 
29
21
  email: mental@rydia.net
30
22
  executables: []
31
23
 
32
24
  extensions:
33
- - ext/extconf.rb
25
+ - ext/retrograph/extconf.rb
34
26
  extra_rdoc_files: []
35
27
 
36
28
  files:
37
29
  - Rakefile
38
30
  - README
31
+ - COPYING
39
32
  - src/retrograph.h
40
33
  - src/retrograph.c
41
- - ext/extconf.rb
42
- - ext/retrograph.c
34
+ - lib/retrograph.rb
35
+ - lib/retrograph/sdl.rb
36
+ - ext/retrograph/extconf.rb
37
+ - ext/retrograph/retrograph.c
43
38
  - spec/retrograph_spec.rb
44
39
  - spec/image-specs/sprites-doubling.case
45
40
  - spec/image-specs/text-colors.case
@@ -79,7 +74,7 @@ post_install_message:
79
74
  rdoc_options: []
80
75
 
81
76
  require_paths:
82
- - ext
77
+ - lib
83
78
  required_ruby_version: !ruby/object:Gem::Requirement
84
79
  requirements:
85
80
  - - ">="
data/ext/extconf.rb DELETED
@@ -1,3 +0,0 @@
1
- require 'mkmf'
2
-
3
- create_makefile('retrograph')