Skip to content

Instantly share code, notes, and snippets.

@katsuyoshi
Last active October 8, 2023 16:11
Show Gist options
  • Save katsuyoshi/7cf6ced16d52996ae81d98097d4d5095 to your computer and use it in GitHub Desktop.
Save katsuyoshi/7cf6ced16d52996ae81d98097d4d5095 to your computer and use it in GitHub Desktop.
DXOpalのテスト
class Charactor
attr_reader :pattern
attr_reader :vsize
attr_reader :image
def initialize pat, vsize = 1
@pattern = pat.each_line.map do |l|
l.split(" ")[1..-1].map{|e| e.to_i(16)}
end.flatten
@vsize = vsize
@width = @pattern.size / @vsize
@height = vsize * 8
@image = Image.new(@width, @height, [255, 0, 0, 0])
idx = 0
@width.times do |ix|
x = ix
y = @height - 1
@vsize.times do |iy|
v = @pattern[idx]
8.times do |b|
if v & 1 == 0
@image[x, y] = [0, 0, 0, 0]
else
@image[x, y] = [255, 255, 255, 255]
end
v >>= 1
y -= 1
end
idx += 1
end
end
end
def sub_charactor offset, length
pat = pattern[offset, length]
Charactor.new("0000: " + pat.map{|c| c.to_s(16).upcase.rjust(2, '0') }.join(" "))
end
def self.shield
@shield ||= begin
pat = <<EOS
1D20: FF 0F FF 1F FF 3F FF 7F FF FF FC FF F8 FF F0 FF F0 FF F0 FF F0 FF
1D36: F0 FF F0 FF F0 FF F8 FF FC FF FF FF FF FF FF 7F FF 3F FF 1F FF 0F
EOS
self.new pat, 2
end
end
def self.flying_saucer
@flying_saucer ||= begin
pat = <<EOS
1D64: 00 00 00 00 04 0C 1E 37 3E 7C 74 7E 7E 74 7C 3E 37 1E 0C 04 00 00 00 00
EOS
self.new pat
end
end
def self.flying_saucer_explosion
@flying_saucer_explosion ||= begin
pat = <<EOS
1D7C: 00 22 00 A5 40 08 98 3D B6 3C 36 1D 10 48 62 B6 1D 98 08 42 90 08 00 00
EOS
self.new pat
end
end
def self.alians
@alians ||= begin
pat = <<EOS
1C00: 00 00 39 79 7A 6E EC FA FA EC 6E 7A 79 39 00 00
1C10: 00 00 00 78 1D BE 6C 3C 3C 3C 6C BE 1D 78 00 00
1C20: 00 00 00 00 19 3A 6D FA FA 6D 3A 19 00 00 00 00
1C30: 00 00 38 7A 7F 6D EC FA FA EC 6D 7F 7A 38 00 00
1C40: 00 00 00 0E 18 BE 6D 3D 3C 3D 6D BE 18 0E 00 00
1C50: 00 00 00 00 1A 3D 68 FC FC 68 3D 1A 00 00 00 00
EOS
self.new pat
end
end
def self.alian point, pat
x = [10, 20, 30].index point
y = [:a, :b].index pat
return nil unless x && y
alians.sub_charactor(16 * (y * 3 + x), 16)
end
def self.charactor code
@fonts ||= begin
pat ||= <<EOS
1E00: 00 1F 24 44 24 1F 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1E08: 00 7F 49 49 49 36 00 00 ; *****... *******. .*****.. *******. *******. *******. .*****.. *******.
1E10: 00 3E 41 41 41 22 00 00 ; ..*..*.. *..*..*. *.....*. *.....*. *..*..*. ...*..*. *.....*. ...*....
1E18: 00 7F 41 41 41 3E 00 00 ; ..*...*. *..*..*. *.....*. *.....*. *..*..*. ...*..*. *.....*. ...*....
1E20: 00 7F 49 49 49 41 00 00 ; ..*..*.. *..*..*. *.....*. *.....*. *..*..*. ...*..*. *.*...*. ...*....
1E28: 00 7F 48 48 48 40 00 00 ; *****... .**.**.. .*...*.. .*****.. *.....*. ......*. ***...*. *******.
1E30: 00 3E 41 41 45 47 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1E38: 00 7F 08 08 08 7F 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1E40: 00 00 41 7F 41 00 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1E48: 00 02 01 01 01 7E 00 00 ; ........ .*...... *******. *******. *******. *******. .*****.. *******.
1E50: 00 7F 08 14 22 41 00 00 ; *.....*. *....... ...*.... *....... .....*.. ....*... *.....*. ...*..*.
1E58: 00 7F 01 01 01 01 00 00 ; *******. *....... ..*.*... *....... ...**... ...*.... *.....*. ...*..*.
1E60: 00 7F 20 18 20 7F 00 00 ; *.....*. *....... .*...*.. *....... .....*.. ..*..... *.....*. ...*..*.
1E68: 00 7F 10 08 04 7F 00 00 ; ........ .******. *.....*. *....... *******. *******. .*****.. ....**..
1E70: 00 3E 41 41 41 3E 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1E78: 00 7F 48 48 48 30 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1E80: 00 3E 41 45 42 3D 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1E88: 00 7F 48 4C 4A 31 00 00 ; .*****.. *******. .*..**.. ......*. .******. ..*****. *******. **...**.
1E90: 00 32 49 49 49 26 00 00 ; *.....*. ...*..*. *..*..*. ......*. *....... .*...... .*...... ..*.*...
1E98: 00 40 40 7F 40 40 00 00 ; *.*...*. ..**..*. *..*..*. *******. *....... *....... ..**.... ...*....
1EA0: 00 7E 01 01 01 7E 00 00 ; .*....*. .*.*..*. *..*..*. ......*. *....... .*...... .*...... ..*.*...
1EA8: 00 7C 02 01 02 7C 00 00 ; *.****.. *...**.. .**..*.. ......*. .******. ..*****. *******. **...**.
1EB0: 00 7F 02 0C 02 7F 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1EB8: 00 63 14 08 14 63 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1EC0: 00 60 10 0F 10 60 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1EC8: 00 43 45 49 51 61 00 00 ; .....**. **....*. .*****.. ........ **...*.. .*....*. ..**.... .*..***.
1ED0: 00 3E 45 49 51 3E 00 00 ; ....*... *.*...*. *.*...*. *....*.. *.*...*. *.....*. ..*.*... *...*.*.
1ED8: 00 00 21 7F 01 00 00 00 ; ****.... *..*..*. *..*..*. *******. *..*..*. *..*..*. ..*..*.. *...*.*.
1EE0: 00 23 45 49 49 31 00 00 ; ....*... *...*.*. *...*.*. *....... *..*..*. *..**.*. *******. *...*.*.
1EE8: 00 42 41 49 59 66 00 00 ; .....**. *....**. .*****.. ........ *...**.. .**..**. ..*..... .***..*.
1EF0: 00 0C 14 24 7F 04 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1EF8: 00 72 51 51 51 4E 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1F00: 00 1E 29 49 49 46 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1F08: 00 40 47 48 50 60 00 00 ; .****... ......*. .**.**.. *...**.. ...*.... ........ ........ ..*.*...
1F10: 00 36 49 49 49 36 00 00 ; *..*.*.. ***...*. *..*..*. *..*..*. ..*.*... *.....*. ........ ..*.*...
1F18: 00 31 49 49 4A 3C 00 00 ; *..*..*. ...*..*. *..*..*. *..*..*. .*...*.. .*...*.. ........ ..*.*...
1F20: 00 08 14 22 41 00 00 00 ; *..*..*. ....*.*. *..*..*. .*.*..*. *.....*. ..*.*... ........ ..*.*...
1F28: 00 00 41 22 14 08 00 00 ; .**...*. .....**. .**.**.. ..****.. ........ ...*.... ........ ..*.*...
1F30: 00 00 00 00 00 00 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1F38: 00 14 14 14 14 14 00 00 ; ........ ........ ........ ........ ........ ........ ........ ........
1F40: 00 22 14 7F 14 22 00 00 ; ........ ........
1F48: 00 03 04 78 04 03 00 00 ; .*...*.. **......
; ..*.*... ..*.....
; *******. ...****.
; ..*.*... ..*.....
; .*...*.. **......
; ........ ........
; ........ ........
; ........ ........
; ........ ........
; ........ ........
; ........ ........
; ........ ........
; ........ ........
; ........ ........
; ........ ........
1FC0: 00 20 40 4D 50 20 00 00 ; 38:"?"
; ........ ........
; ........ ........
; ........ ........
; ........ ........
; ........ ........
; ........ ........
1FF8: 00 08 08 08 08 08 00 00 ; 3F:"-"
EOS
pat.each_line.select{|l| l.include?(";")}.map do |l|
self.new l.split(/;/).first
end
end
@fonts[code]
end
end
require_remote 'vram.rb'
require_remote 'charactor.rb'
require_remote 'message.rb'
class Uses
attr_reader :inv
MODE_BEGIN_WAIT_1 = 0
MODE_WAIT_1 = 1
MODE_BEGIN_PLAY = 2
MODE_PLAY = 3
MODE_BEGIN_SPACE_INVADERS = 4
MODE_SPACE_INVADERS = 5
MODE_BEGIN_WAIT_2 = 6
MODE_WAIT_2 = 7
MODE_BEGIN_SCORE_ADVANCE_TABLE = 8
MODE_SCORE_ADVANCE_TABLE = 9
MODE_BEGIN_WAIT_3 = 10
MODE_WAIT_3 = 11
MODE_BEGIN_MYSTERY_SCORE = 12
MODE_MYSTERY_SCORE = 13
MODE_BEGIN_30_SCORE = 14
MODE_30_SCORE = 15
MODE_BEGIN_20_SCORE = 16
MODE_20_SCORE = 17
MODE_BEGIN_10_SCORE = 18
MODE_10_SCORE = 19
MODE_BEGIN_WAIT_4 = 20
MODE_WAIT_4 = 21
MODE_END = 22
def initialize
@inv = false
reset
end
def step game
now = Time.now
return true unless @duration == 0 || now - @start_at >= @duration
case @mode
when MODE_BEGIN_WAIT_1, MODE_BEGIN_WAIT_2, MODE_BEGIN_WAIT_3, MODE_BEGIN_WAIT_4
begin_wait 0x40
@mode += 1
when MODE_BEGIN_WAIT_4
begin_wait 0x80
@mode += 1
when MODE_WAIT_1, MODE_WAIT_2, MODE_WAIT_3, MODE_WAIT_4
begin_wait 0.0
@mode += 1
when MODE_BEGIN_PLAY
mes = inv ? Message.play_inv : Message.play
begin_to_show_message game, mes, 0x3017
@mode += 1
when MODE_BEGIN_SPACE_INVADERS
mes = Message.space_invaders
begin_to_show_message game, mes, 0x2B14
@mode += 1
when MODE_BEGIN_SCORE_ADVANCE_TABLE
mes = Message.score_advance_table
mes.draw *game.conv_xy(0x2810), game.vram.image
[
[Charactor.flying_saucer.sub_charactor(4, 16), 0x2C0E],
[Charactor.alian(30, :a), 0x2C0C],
[Charactor.alian(20, :b), 0x2C0A],
[Charactor.alian(10, :a), 0x2C08],
].each do |char, address|
x, y = game.conv_xy(address)
game.vram.image.draw(x, y, char.image)
end
begin_wait 0.0
@mode += 1
when MODE_PLAY, MODE_SPACE_INVADERS, MODE_SCORE_ADVANCE_TABLE,
MODE_MYSTERY_SCORE, MODE_10_SCORE, MODE_20_SCORE, MODE_30_SCORE
c = @codes[@code_ptr]
if c == nil
@mode += 1
else
@code_ptr += 1
img = Charactor.charactor(c).image
game.vram.image.draw(@x, @y, img)
@x += 8
begin_wait 7
end
when MODE_BEGIN_MYSTERY_SCORE
mes = Message.mystery
begin_to_show_message game, mes, 0x2E0E
@mode += 1
when MODE_BEGIN_30_SCORE
mes = Message.thirty_points
begin_to_show_message game, mes, 0x2E0C
@mode += 1
when MODE_BEGIN_20_SCORE
mes = Message.twenty_points
begin_to_show_message game, mes, 0x2E0A
@mode += 1
when MODE_BEGIN_10_SCORE
mes = Message.ten_points
begin_to_show_message game, mes, 0x2E08
@mode += 1
when MODE_END
@inv = !@inv
@mode = MODE_BEGIN_WAIT_1
return false
else
return false
end
true
end
private
def reset
@mode = MODE_BEGIN_WAIT_1
begin_wait 0.0
end
def begin_wait count
duration = count.to_f / 0x40.to_f
@start_at = Time.now
@duration = duration
end
def begin_to_show_message game, mes, address
@codes = mes.codes
@code_ptr = 0
@x, @y = game.conv_xy(address)
end
end
class Game
attr_reader :width, :height
attr_reader :scores, :credit
attr_reader :mode
attr_reader :uses
attr_reader :vram
PLAYER_1 = 0
PLAYER_2 = 1
HIGH_SCORE = 2
MODE_INIT = 0
MODE_USES = 1
def initialize
@width = 244
@height = 256
@scores = [0] * 3
@credit = 0
@mode = MODE_INIT
@uses = Uses.new
reset
end
def image
@vram.image
end
def step
case mode
when MODE_INIT
draw_score
draw_credit
@mode = MODE_USES
when MODE_USES
r = @uses.step self
unless r
reset
@mode = MODE_INIT unless r
end
end
end
def conv_xy address
y = 32 * 8 - ((address - 0x2400) % 32) * 8# + 8
x = ((address - 0x2400) / (32 * 8)).to_i * 8 + 8
[x, y]
end
private
def draw_score
Message.score_titles.draw(*conv_xy(0x241E), @vram.image)
draw_number 0x2F1C, scores[PLAYER_1]
draw_number 0x391C, scores[PLAYER_2]
draw_number 0x271C, scores[HIGH_SCORE]
end
def draw_credit
Message.credit.draw(*conv_xy(0x3501), @vram.image)
draw_number 0x3C01, credit, 2
end
def draw_number address, number, digits=5
codes = number.to_s.rjust(digits, '0').split(//).map{|e| e.to_i + 0x1a}
x, y = conv_xy address
codes.each do |c|
img = Charactor.charactor(c).image
@vram.image.draw(x, y, img)
x += 8
end
end
def reset
@vram = VRam.new(@width, @height)
reset_shields
end
def reset_shields
@shields = 4.times.map{|i| Charactor.shield.dup }
end
end
require 'dxopal'
require_remote 'game.rb'
include DXOpal
class FpsMeasure
attr_reader :fps, :game
def initialize
@from = Time.new
@fps = 0
end
def measure
now = Time.new
@fps = 1.0 / (now - @from)
@from = now
end
end
@game = Game.new
Window.load_resources do
@fps_meas = FpsMeasure.new
Window.bgcolor = C_BLACK
Window.width = @game.width
Window.height = @game.height + 8 * 2 + 20
@fps_meas.measure
Window.loop do
@game.step
Window.draw(0, 0, @game.image)
@fps_meas.measure
Window.draw_font(0, @game.height + 8, "FPS #{"%.1f" % @fps_meas.fps}", Font.default, color: C_WHITE)
end
end
require_remote 'charactor.rb'
class Message
attr_reader :codes
def initialize codes
@codes = codes
end
def self.score_titles
@score_titles ||= begin
pat = <<EOS
1AE4: 26 12 02 0E 11 04 24 1B 25 26 07 08
1AF0: 3F 12 02 0E 11 04 26 12 02 0E 11 04
1AFC: 24 1C 25 26
EOS
codes = pat.each_line.map do |l|
l.strip.split(/ /)[1..-1].map{|l| l.to_i(16)}
end.flatten
self.new codes
end
end
def self.credit
@credit ||= begin
pat = <<EOS
1FA9: 02 11 04 03 08 13 26
EOS
codes = pat.each_line.map do |l|
l.strip.split(/ /)[1..-1].map{|l| l.to_i(16)}
end.flatten
self.new codes
end
end
def self.play
@play ||= begin
pat = <<EOS
1DAB: 0F 0B 00 18 ; "PLAY" with normal Y
EOS
codes = pat.each_line.map do |l|
l.strip.split(";").first.split(/ /)[1..-1].map{|l| l.to_i(16)}
end.flatten
self.new codes
end
end
def self.play_inv
@play_inv ||= begin
pat = <<EOS
1CFA: 0F 0B 00 29 ; "PLAy" with an upside down 'Y' for splash screen
EOS
codes = pat.each_line.map do |l|
l.strip.split(";").first.split(/ /)[1..-1].map{|l| l.to_i(16)}
end.flatten
self.new codes
end
end
def self.space_invaders
@space_invaders ||= begin
pat = <<EOS
1DAF: 12 0F 00 02 04 26 26 08 0D 15 00 03 04 11 12
EOS
codes = pat.each_line.map do |l|
l.strip.split(";").first.split(/ /)[1..-1].map{|l| l.to_i(16)}
end.flatten
self.new codes
end
end
def self.score_advance_table
@score_advance_table ||= begin
pat = <<EOS
1CA3: 28 12 02 0E 11 04 26 00
1CAB: 03 15 00 0D 02 04 26 13
1CB3: 00 01 0B 04 28
EOS
codes = pat.each_line.map do |l|
l.strip.split(";").first.split(/ /)[1..-1].map{|l| l.to_i(16)}
end.flatten
self.new codes
end
end
def self.mystery
@mystery ||= begin
pat = <<EOS
1DE0: 27 38 26 0C 18 12 13 04 11 18 ; "=? MYSTERY"
EOS
codes = pat.each_line.map do |l|
l.strip.split(";").first.split(/ /)[1..-1].map{|l| l.to_i(16)}
end.flatten
self.new codes
end
end
def self.thirty_points
@thirty_points ||= begin
pat = <<EOS
1DEA: 27 1D 1A 26 0F 0E 08 0D 13 12 ; "=30 POINTS"
EOS
codes = pat.each_line.map do |l|
l.strip.split(";").first.split(/ /)[1..-1].map{|l| l.to_i(16)}
end.flatten
self.new codes
end
end
def self.twenty_points
@twenty_points ||= begin
pat = <<EOS
1DF4: 27 1C 1A 26 0F 0E 08 0D 13 12 ; "=20 POINTS"
EOS
codes = pat.each_line.map do |l|
l.strip.split(";").first.split(/ /)[1..-1].map{|l| l.to_i(16)}
end.flatten
self.new codes
end
end
def self.ten_points
@ten_points ||= begin
pat = <<EOS
1C99: 27 1B 1A 26 0F 0E 08 0D 13 12 ; "=10 POINTS"
EOS
codes = pat.each_line.map do |l|
l.strip.split(";").first.split(/ /)[1..-1].map{|l| l.to_i(16)}
end.flatten
self.new codes
end
end
def draw x, y, image
draw_with_duration x, y, image
end
def draw_with_duration x, y, image, duration = 0
codes.each do |c|
img = Charactor.charactor(c).image
image.draw(x, y, img)
x += 8
sleep(duration) unless duration == 0
end
end
end
class VRam
attr_reader :width, :height, :image
def initialize(width, height)
@width = width
@height = height
@vram_size = @width / 8 * @height
@vram = Array.new(@vram_size, 0)
@image = Image.new(@width, @height, [255, 0, 0, 0])
end
def draw(w)
w.draw(0, 0, @image)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment