retrograph 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/README +248 -0
  2. data/Rakefile +54 -0
  3. data/ext/extconf.rb +3 -0
  4. data/ext/retrograph.c +275 -0
  5. data/spec/image-specs/color-blue-bits.case +2 -0
  6. data/spec/image-specs/color-blue-bits.png +0 -0
  7. data/spec/image-specs/color-green-bits.case +2 -0
  8. data/spec/image-specs/color-green-bits.png +0 -0
  9. data/spec/image-specs/color-red-bits.case +2 -0
  10. data/spec/image-specs/color-red-bits.png +0 -0
  11. data/spec/image-specs/default-screen-black.case +1 -0
  12. data/spec/image-specs/default-screen-black.png +0 -0
  13. data/spec/image-specs/default-screen-color-0.case +2 -0
  14. data/spec/image-specs/default-screen-color-0.png +0 -0
  15. data/spec/image-specs/scroll-mode1.case +7 -0
  16. data/spec/image-specs/scroll-mode1.png +0 -0
  17. data/spec/image-specs/scroll-mode2.case +10 -0
  18. data/spec/image-specs/scroll-mode2.png +0 -0
  19. data/spec/image-specs/scroll-mode3.case +10 -0
  20. data/spec/image-specs/scroll-mode3.png +0 -0
  21. data/spec/image-specs/sprites-doubling.case +5 -0
  22. data/spec/image-specs/sprites-doubling.png +0 -0
  23. data/spec/image-specs/sprites-mode2.case +8 -0
  24. data/spec/image-specs/sprites-mode2.png +0 -0
  25. data/spec/image-specs/sprites-mode3.case +8 -0
  26. data/spec/image-specs/sprites-mode3.png +0 -0
  27. data/spec/image-specs/sprites-ordering.case +5 -0
  28. data/spec/image-specs/sprites-ordering.png +0 -0
  29. data/spec/image-specs/text-colors.case +5 -0
  30. data/spec/image-specs/text-colors.png +0 -0
  31. data/spec/image-specs/tile-attributes-mode2.case +9 -0
  32. data/spec/image-specs/tile-attributes-mode2.png +0 -0
  33. data/spec/image-specs/tile-attributes-mode3.case +9 -0
  34. data/spec/image-specs/tile-attributes-mode3.png +0 -0
  35. data/spec/retrograph_spec.rb +133 -0
  36. data/src/retrograph.c +695 -0
  37. data/src/retrograph.h +66 -0
  38. metadata +103 -0
data/README ADDED
@@ -0,0 +1,248 @@
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 requires Ruby/SDL and outputs to an SDL
10
+ surface.
11
+
12
+ == Feature Overview ==
13
+
14
+ * 256x244 pixel display
15
+ * 16k VRAM
16
+ * 40x25 and 32x28 text modes
17
+ * 2bpp and 4bpp graphics modes
18
+ * 64 colors total
19
+ * 31 simultaneous colors (16 background, 15 sprite)
20
+ * 32x32 background layer
21
+ * 64 sprites, max 8 per scanline
22
+
23
+ == Usage ==
24
+
25
+ Retrograph's simulated Video Display Unit (or VDU) is
26
+ represented by an instance of the Retrograph::VDU class.
27
+ You can interact with the VDU by writing byte strings
28
+ into its memory using Retrograph::VDU#write, which takes
29
+ a start address (in the VDU's 16-bit address space) and
30
+ a string to write.
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.
36
+
37
+ 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")
49
+ end
50
+
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
+ == Important Notes ==
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.
66
+
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.
79
+
80
+ In text modes, you will need to load your own font into the
81
+ pattern table; the VDU does not provide one by default.
82
+
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
+ Sprites patterns are always 4bpp, even in the 2bpp mode.
90
+ In Graphics A, one half of VRAM is occupied by 512 2bpp
91
+ patterns (for the background), and the other half by 256
92
+ 4bpp patterns for sprites. In Graphics B, the entirety
93
+ of VRAM is occupied by just 512 4bpp patterns, with the
94
+ upper half of those available to sprites.
95
+
96
+ When scrolling horizontally, the leftmost background
97
+ column will not be displayed if it is partway offscreen.
98
+
99
+ == VDU Memory Map ==
100
+
101
+ $0000 - $3FFF: VRAM (16 kbytes)
102
+ $4000 - $7DFF: VRAM (mirror) (16 kbytes - 512 bytes)
103
+ $7E00 - $7EFF: Sprite Table* (256 bytes)
104
+ $7F00 - $7F0F: BG Palette (16 bytes)
105
+ $7F10 - $7F1F: BG/Sprite Palette* (16 bytes)
106
+ $7F20 - $7FF7: unused (216 bytes)
107
+ $7FF8 - $7FFF: VDU Registers (8 bytes)
108
+ $7FF8: Flags
109
+ 4-7: unused
110
+ 3: Enable Sprites*
111
+ 2: Enable BG
112
+ 0-1: Mode
113
+ 00 - Text A (40x25)
114
+ 01 - Text B (32x28)
115
+ 10 - Graphics A (2bpp)
116
+ 11 - Graphics B (4bpp)
117
+ $7FF9: Pattern Table Base
118
+ Text A + B:
119
+ addr = (base & 0b00110000) << 8
120
+ base = $00, $10, $20, or $30
121
+ addr = $0000, $1000, $2000, or
122
+ $3000
123
+ Graphics A + B:
124
+ addr = (base & 0b00100000) << 8
125
+ base = $00 or $20
126
+ addr = $0000 or $2000
127
+ $7FFA: Name Table Base
128
+ addr = ((base & 0b00110000) |
129
+ 0b00001000) << 8
130
+ base = $00, $10, $20, or $30
131
+ addr = $0800, $1800, $2800, or
132
+ $3800
133
+ $7FFB: unused
134
+ $7FFC: BG scroll X*
135
+ $7FFD: BG scroll Y**
136
+ $7FFE-7FFF: unused
137
+ $8000 - $FFFF: mirror of $0000 - $7FFF
138
+
139
+ Notes:
140
+ *Ignored in text modes
141
+ **In text modes, the lower 3 bits of the vertical scroll
142
+ register are ignored and it wraps at 200 (Text A) or
143
+ 224 (Text B)
144
+
145
+ === Pattern Table ===
146
+
147
+ The starting address of the pattern table is determined by
148
+ register $7FF9.
149
+
150
+ The rows constituting each pattern in the table are given
151
+ in sequence from top to bottom. Within each byte, the
152
+ leftmost pixel is in the least significant position.
153
+
154
+ Text A: 256 patterns * 8 bytes per pattern = 2k
155
+ 6x8 pixel patterns
156
+ 1 byte per pattern row, 1 bit per pixel
157
+ most significant 2 bits ignored
158
+
159
+ Text B: 256 patterns * 8 bytes per pattern = 2k
160
+ 8x8 pixel patterns
161
+ 1 byte per patern row, 1 bit per pixel
162
+
163
+ Graphics A: 512 bg patterns * 16 bytes per pattern +
164
+ 256 sprite patterns * 32 bytes/pattern = 16k
165
+ 8x8 pixel patterns
166
+ 2 bytes/pattern row, 2 bits/pixel (bg)
167
+ 4 bytes/pattern row, 4 bits/pixel (sprites)
168
+
169
+ Graphics B: 512 patterns * 32 bytes per pattern = 16k
170
+ 8x8 pixel patterns
171
+ 4 bytes/pattern row, 4 bits/pixel
172
+ last 256 patterns shared with sprites
173
+
174
+ === Name Table ===
175
+
176
+ The starting address of the name table is determined by
177
+ register $7FFA.
178
+
179
+ Table sizes:
180
+
181
+ Text A: 40x25 characters * 2 bytes/character = 2000 bytes
182
+ Text B: 32x28 characters * 2 bytes/character = 1792 bytes
183
+ Graphics A + B: 32x32 tiles * 2 bytes per tile = 2k
184
+
185
+ Cell formats:
186
+
187
+ Text: pppppppp bbbbffff
188
+ p - pattern index
189
+ f - foreground color
190
+ b - background color
191
+
192
+ Graphics: pppppppp -bhvcccP
193
+ p - low byte of pattern index
194
+ P - high bit of pattern index
195
+ c - high bits of color
196
+ h - horizontal flip
197
+ v - vertical flip
198
+ b - background priority
199
+
200
+ In graphics modes, the color is computed by taking the high
201
+ color bits and oring them with the pattern color to get a
202
+ color in the range 0-31 (with the upper 16 colors coming
203
+ from the sprite palette):
204
+
205
+ color = pattern_color | (high_bits << 2)
206
+
207
+ (However, a pattern color of 0 is always transparent
208
+ regardless of the high color bits.)
209
+
210
+ === Sprite Table ===
211
+
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.
215
+
216
+ 4 bytes * 64 sprites = 256 bytes
217
+
218
+ xxxxxxxx yyyyyyyy pppppppp --hvHVcc
219
+
220
+ x - X position (left edge)
221
+ y - Y position + 1 (top edge) (0 = hidden)
222
+ p - pattern index
223
+ h - horizontal flip
224
+ v - vertical flip
225
+ H - double size horizontally
226
+ V - double size vertically
227
+ c - high bits of color
228
+
229
+ Sprite colors are computed as follows:
230
+
231
+ color = pattern_color | (high_bits << 2) | 16;
232
+
233
+ (As with tiles, a pattern color of 0 is always transparent.)
234
+
235
+ === Palette Table ===
236
+
237
+ The palette table always begins at $7F00.
238
+
239
+ 1 byte * 32 colors (16 bg, 16 bg/sprite)
240
+
241
+ --rrggbb
242
+
243
+ r - red
244
+ g - green
245
+ b - blue
246
+
247
+ Note that the first sprite color (color 16) is effectively
248
+ never used.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require 'spec/rake/spectask'
4
+ require 'rake/clean'
5
+
6
+ GEM_VERSION = '0.2.1'
7
+
8
+ CLEAN.include("**/*.o")
9
+ CLEAN.include("**/*.so")
10
+ CLEAN.include("spec/**/*-output.bmp")
11
+ CLOBBER.include("ext/Makefile")
12
+
13
+ task :clobber => [:clean]
14
+
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
+ gemspec = Gem::Specification.new do |gemspec|
26
+ gemspec.name = "retrograph"
27
+ gemspec.version = GEM_VERSION
28
+ gemspec.author = "MenTaLguY <mental@rydia.net>"
29
+ gemspec.summary = "Retrograph, a retrodisplay."
30
+ 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
+ EOS
34
+ gemspec.homepage = "http://rubyforge.org/projects/retrograph"
35
+ gemspec.email = "mental@rydia.net"
36
+ 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
+ 'spec/**/*_spec.rb', 'spec/**/*.case',
41
+ 'spec/**/*.png']
42
+ gemspec.extensions = ['ext/extconf.rb']
43
+ gemspec.require_paths = ['ext']
44
+ gemspec.platform = Gem::Platform::RUBY
45
+ gemspec.add_dependency("rubysdl", ">= 1.3.0")
46
+ end
47
+
48
+ Rake::GemPackageTask.new(gemspec) do |pkg|
49
+ pkg.need_tar = true
50
+ end
51
+
52
+ task :spec => [:build]
53
+ task :package => [:clean, :spec]
54
+ task :default => [:clean, :spec]
data/ext/extconf.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile('retrograph')
data/ext/retrograph.c ADDED
@@ -0,0 +1,275 @@
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
+ #include <intern.h>
25
+ #include <string.h>
26
+
27
+ #include "../src/retrograph.h"
28
+ #include "../src/retrograph.c"
29
+
30
+ /* First few fields of SDL_Surface */
31
+ typedef struct surface_header_tag {
32
+ uint32_t flags;
33
+ struct pixelformat_tag *format; /* abstract */
34
+ int w, h;
35
+ uint16_t pitch;
36
+ void *pixels;
37
+ } surface_header_t;
38
+
39
+ /* for Ruby/SDL 2.x, which indirectly wraps surfaces */
40
+ typedef struct rubysdl2_surface_tag {
41
+ surface_header_t *surface;
42
+ } rubysdl2_surface_t;
43
+
44
+ typedef struct vdu_wrapper_tag {
45
+ retrograph_vdu_t vdu;
46
+ VALUE surface;
47
+ int scanline;
48
+ } vdu_wrapper_t;
49
+
50
+ #define NOT_IN_FRAME -1
51
+
52
+ static VALUE mRetrograph = Qnil;
53
+ static VALUE cVDU = Qnil;
54
+ static VALUE eRenderError = Qnil;
55
+ static VALUE mSDL = Qnil;
56
+ static VALUE cSurface = Qnil;
57
+ static VALUE CREATE_SURFACE_METHOD_NAME = Qnil;
58
+
59
+ static surface_header_t *get_rubysdl_surface_1_x(VALUE surface_r) {
60
+ surface_header_t *surface;
61
+ Data_Get_Struct(surface_r, surface_header_t, surface);
62
+ return surface;
63
+ }
64
+
65
+ static surface_header_t *get_rubysdl_surface_2_x(VALUE surface_r) {
66
+ rubysdl2_surface_t *surface;
67
+ Data_Get_Struct(surface_r, rubysdl2_surface_t, surface);
68
+ return surface->surface;
69
+ }
70
+
71
+ static surface_header_t *(*get_rubysdl_surface)(VALUE surface_r) = NULL;
72
+
73
+ static void check_errors(retrograph_vdu_t vdu) {
74
+ retrograph_error_t error;
75
+ error = retrograph_get_error(vdu);
76
+ switch (error) {
77
+ case RETROGRAPH_OK:
78
+ return;
79
+ case RETROGRAPH_NO_MEMORY:
80
+ rb_memerror();
81
+ case RETROGRAPH_INVALID_ARGUMENT:
82
+ rb_raise(rb_const_get(rb_cObject, rb_intern("ArgumentError")),
83
+ "Invalid argument");
84
+ default:
85
+ rb_raise(rb_eRuntimeError, "Unknown error");
86
+ }
87
+ }
88
+
89
+ static void mark_vdu(vdu_wrapper_t *vdu) {
90
+ rb_gc_mark(vdu->surface);
91
+ }
92
+
93
+ static void free_vdu(vdu_wrapper_t *vdu) {
94
+ if (vdu->vdu) {
95
+ retrograph_destroy(vdu->vdu);
96
+ }
97
+ xfree(vdu);
98
+ }
99
+
100
+ static VALUE rescue_free_vdu(VALUE data, VALUE exc) {
101
+ free_vdu((vdu_wrapper_t *)data);
102
+ rb_exc_raise(exc);
103
+ return Qnil;
104
+ }
105
+
106
+ static VALUE alloc_vdu2(VALUE data);
107
+
108
+ static VALUE alloc_vdu(VALUE klass) {
109
+ vdu_wrapper_t *vdu;
110
+ VALUE vdu_r;
111
+ vdu = ALLOC(vdu_wrapper_t);
112
+ vdu->vdu = NULL;
113
+ vdu->surface = Qnil;
114
+ vdu->scanline = NOT_IN_FRAME;
115
+ rb_rescue2(alloc_vdu2, (VALUE)vdu,
116
+ rescue_free_vdu, (VALUE)vdu,
117
+ rb_eException, (VALUE)0);
118
+ return Data_Wrap_Struct(klass, mark_vdu, free_vdu, vdu);
119
+ }
120
+
121
+ VALUE alloc_vdu2(VALUE data) {
122
+ vdu_wrapper_t *vdu=(vdu_wrapper_t *)data;
123
+ VALUE surface;
124
+ surface = rb_funcall(cSurface, SYM2ID(CREATE_SURFACE_METHOD_NAME), 8,
125
+ INT2FIX(0), /* flags */
126
+ INT2FIX(RETROGRAPH_DISPLAY_WIDTH),
127
+ INT2FIX(RETROGRAPH_DISPLAY_HEIGHT),
128
+ INT2FIX(RETROGRAPH_BITS_PER_PIXEL),
129
+ INT2FIX(RETROGRAPH_RED_MASK),
130
+ INT2FIX(RETROGRAPH_GREEN_MASK),
131
+ INT2FIX(RETROGRAPH_BLUE_MASK),
132
+ INT2FIX(RETROGRAPH_ALPHA_MASK));
133
+ vdu->surface = surface;
134
+ vdu->vdu = retrograph_new();
135
+ check_errors(vdu->vdu);
136
+ return Qnil;
137
+ }
138
+
139
+ static VALUE vdu_write(VALUE self, VALUE start_address_r, VALUE data_r) {
140
+ vdu_wrapper_t *vdu;
141
+ retrograph_addr_t start_address;
142
+ Check_Type(data_r, T_STRING);
143
+ Data_Get_Struct(self, vdu_wrapper_t, vdu);
144
+ 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);
147
+ return Qnil;
148
+ }
149
+
150
+ static VALUE vdu_get_surface(VALUE self) {
151
+ vdu_wrapper_t *vdu;
152
+ Data_Get_Struct(self, vdu_wrapper_t, vdu);
153
+ return vdu->surface;
154
+ }
155
+
156
+ static VALUE vdu_render_scanlines(VALUE self, VALUE n_scanlines_r) {
157
+ vdu_wrapper_t *vdu;
158
+ surface_header_t *surface;
159
+ uint8_t *current_line;
160
+ int max_scanline;
161
+
162
+ Data_Get_Struct(self, vdu_wrapper_t, vdu);
163
+
164
+ if (vdu->scanline == NOT_IN_FRAME) {
165
+ rb_raise(eRenderError, "Not in frame");
166
+ }
167
+
168
+ surface = get_rubysdl_surface(vdu->surface);
169
+
170
+ max_scanline = (int)NUM2UINT(n_scanlines_r) + vdu->scanline;
171
+ if (max_scanline > RETROGRAPH_DISPLAY_HEIGHT) {
172
+ max_scanline = RETROGRAPH_DISPLAY_HEIGHT;
173
+ }
174
+
175
+ current_line = (uint8_t *)surface->pixels + vdu->scanline * surface->pitch;
176
+ for (; vdu->scanline < max_scanline; vdu->scanline++) {
177
+ retrograph_render_scanline(vdu->vdu, current_line,
178
+ RETROGRAPH_DISPLAY_WIDTH *
179
+ RETROGRAPH_BYTES_PER_PIXEL);
180
+ check_errors(vdu->vdu);
181
+ current_line += surface->pitch;
182
+ }
183
+
184
+ return Qnil;
185
+ }
186
+
187
+ static VALUE vdu_render_frame_inner(VALUE self) {
188
+ vdu_wrapper_t *vdu;
189
+ Data_Get_Struct(self, vdu_wrapper_t, vdu);
190
+ retrograph_begin_frame(vdu->vdu);
191
+ check_errors(vdu->vdu);
192
+ vdu->scanline = 0;
193
+ if (rb_block_given_p()) {
194
+ rb_yield(Qundef);
195
+ }
196
+ vdu_render_scanlines(self, INT2FIX(RETROGRAPH_DISPLAY_HEIGHT));
197
+ return Qnil;
198
+ }
199
+
200
+ static VALUE vdu_render_frame_cleanup(VALUE self) {
201
+ vdu_wrapper_t *vdu;
202
+ Data_Get_Struct(self, vdu_wrapper_t, vdu);
203
+ vdu->scanline = NOT_IN_FRAME;
204
+ return Qnil;
205
+ }
206
+
207
+ static VALUE vdu_render_frame(VALUE self) {
208
+ vdu_wrapper_t *vdu;
209
+ int block_given;
210
+ Data_Get_Struct(self, vdu_wrapper_t, vdu);
211
+ block_given = rb_block_given_p();
212
+ if (vdu->scanline != NOT_IN_FRAME) {
213
+ rb_raise(eRenderError, "Already in frame");
214
+ }
215
+ rb_ensure(vdu_render_frame_inner, self, vdu_render_frame_cleanup, self);
216
+ return vdu->surface;
217
+ }
218
+
219
+ void Init_retrograph(void) {
220
+ VALUE rubysdl_version;
221
+ rb_require("sdl");
222
+
223
+ rb_global_variable(&mSDL);
224
+ mSDL = rb_const_get(rb_cObject, rb_intern("SDL"));
225
+ rb_global_variable(&cSurface);
226
+ cSurface = rb_const_get(mSDL, rb_intern("Surface"));
227
+
228
+ rubysdl_version = rb_const_get(mSDL, rb_intern("VERSION"));
229
+ rb_global_variable(&CREATE_SURFACE_METHOD_NAME);
230
+ if (!strncmp(StringValueCStr(rubysdl_version), "1.", 2)) {
231
+ CREATE_SURFACE_METHOD_NAME = ID2SYM(rb_intern("new"));
232
+ get_rubysdl_surface = get_rubysdl_surface_1_x;
233
+ } else if (!strncmp(StringValueCStr(rubysdl_version), "2.", 2)) {
234
+ CREATE_SURFACE_METHOD_NAME = ID2SYM(rb_intern("createWithFormat"));
235
+ get_rubysdl_surface = get_rubysdl_surface_2_x;
236
+ } else {
237
+ rb_raise(rb_eLoadError, "Unrecognized Ruby/SDL version");
238
+ }
239
+
240
+ rb_global_variable(&mRetrograph);
241
+ mRetrograph = rb_define_module("Retrograph");
242
+
243
+ #define DEFINE_CONSTANT(base_name) \
244
+ rb_const_set(mRetrograph, rb_intern(#base_name), \
245
+ INT2FIX(RETROGRAPH_##base_name))
246
+
247
+ DEFINE_CONSTANT(DISPLAY_WIDTH);
248
+ DEFINE_CONSTANT(DISPLAY_HEIGHT);
249
+ DEFINE_CONSTANT(BYTES_PER_PIXEL);
250
+ DEFINE_CONSTANT(BITS_PER_PIXEL);
251
+
252
+ DEFINE_CONSTANT(RED_SHIFT);
253
+ DEFINE_CONSTANT(GREEN_SHIFT);
254
+ DEFINE_CONSTANT(BLUE_SHIFT);
255
+
256
+ DEFINE_CONSTANT(RED_MASK);
257
+ DEFINE_CONSTANT(GREEN_MASK);
258
+ DEFINE_CONSTANT(BLUE_MASK);
259
+ DEFINE_CONSTANT(ALPHA_MASK);
260
+
261
+ #undef DEFINE_CONSTANT
262
+
263
+ rb_global_variable(&cVDU);
264
+ cVDU = rb_define_class_under(mRetrograph, "VDU", rb_cObject);
265
+
266
+ rb_global_variable(&eRenderError);
267
+ eRenderError = rb_define_class_under(mRetrograph, "RenderError",
268
+ rb_eRuntimeError);
269
+
270
+ rb_define_alloc_func(cVDU, alloc_vdu);
271
+ 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);
275
+ }
@@ -0,0 +1,2 @@
1
+ should use color bits 0-1 for blue
2
+ 7f00: 03
@@ -0,0 +1,2 @@
1
+ should use color bits 2-3 for green
2
+ 7f00: 0c
@@ -0,0 +1,2 @@
1
+ should use color bits 4-5 for red
2
+ 7f00: 30
Binary file
@@ -0,0 +1 @@
1
+ should fill the screen with black by default
@@ -0,0 +1,2 @@
1
+ should fill the screen with color 0 by default
2
+ 7f00: ff
@@ -0,0 +1,7 @@
1
+ should support text-style scrolling in mode 1
2
+ 7ff8: 05
3
+ 7ffc: 0c 0c
4
+ 7f00: 00 ff
5
+ 0008: 01 03 07 0f 1f 3f 7f ff ff 7f 3f 1f 0f 07 03 01
6
+ 0800: 0101
7
+ 0840: 0102
Binary file
@@ -0,0 +1,10 @@
1
+ should support smooth scrolling in mode 2
2
+ 7ff8: 06
3
+ 7ffc: 0c 84
4
+ 7f00: 00 ff
5
+ 0010: 0001 0005 0015 0055 0155 0555 1555 5555
6
+ 0020: 5555 1555 0555 0155 0055 0015 0005 0001
7
+ 0030: 1111 4444 1111 4444 1111 4444 1111 4444
8
+ 0c00: 0002
9
+ 0ac0: 0001
10
+ 0b00: 0003
Binary file
@@ -0,0 +1,10 @@
1
+ should support smooth scrolling in mode 3
2
+ 7ff8: 07
3
+ 7ffc: 0c 84
4
+ 7f00: 00 ff
5
+ 0020: 00000001 00000011 00000111 00001111 00011111 00111111 01111111 11111111
6
+ 0040: 11111111 01111111 00111111 00011111 00001111 00000111 00000011 00000001
7
+ 0060: 01010101 10101010 01010101 10101010 01010101 10101010 01010101 10101010
8
+ 0c00: 0002
9
+ 0ac0: 0001
10
+ 0b00: 0003
Binary file
@@ -0,0 +1,5 @@
1
+ should render doubled sprites
2
+ 7ff8: 0b
3
+ 7f10: 00 3f
4
+ 7e00: 00 01 0001 10 01 0801 00 11 0401 10 11 0c01
5
+ 2020: 10101010 01010101 10101010 01010101 10101010 01010101 10101010 01010101
@@ -0,0 +1,8 @@
1
+ should show first 8 sprites per line in mode 2
2
+ 7ff8: 0a
3
+ 7f10: 00 02 08 0a 20 22 38 2a 15 17 1d 1f 35 37 3d 3f
4
+ 7e00: 00 01 0001 08 01 0002 10 01 0101 18 01 0102
5
+ 7e10: 20 01 0201 28 01 0202 30 01 0301 38 01 0302
6
+ 7e20: 40 01 0001 48 01 0002 50 01 0001 58 01 0002
7
+ 2020: 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
8
+ 2040: 20202020 02020202 20202020 02020202 20202020 02020202 20202020 02020202
Binary file
@@ -0,0 +1,8 @@
1
+ should show first 8 sprites per line in mode 3
2
+ 7ff8: 0b
3
+ 7f10: 00 02 08 0a 20 22 38 2a 15 17 1d 1f 35 37 3d 3f
4
+ 7e00: 00 01 0001 08 01 0002 10 01 0101 18 01 0102
5
+ 7e10: 20 01 0201 28 01 0202 30 01 0301 38 01 0302
6
+ 7e20: 40 01 0001 48 01 0002 50 01 0001 58 01 0002
7
+ 2020: 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
8
+ 2040: 20202020 02020202 20202020 02020202 20202020 02020202 20202020 02020202
Binary file