gruesome 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,148 +9,148 @@ require_relative 'header'
9
9
  require_relative 'zscii'
10
10
 
11
11
  module Gruesome
12
- module Z
13
- class Dictionary
14
- def initialize(memory)
15
- @memory = memory
16
- @header = Header.new(@memory.contents)
17
-
18
- @abbreviation_table = AbbreviationTable.new(@memory)
19
-
20
- @lookup_cache = {}
21
- end
22
-
23
- def address
24
- @header.dictionary_addr
25
- end
26
-
27
- def address_of_entries
28
- addr = self.address
29
- num_input_codes = @memory.force_readb(addr)
30
- addr += 1
31
- addr += num_input_codes
32
- addr += 3
33
-
34
- addr
35
- end
36
-
37
- def entry_length
38
- addr = self.address_of_entries - 3
39
- return @memory.force_readb(addr)
40
- end
41
-
42
- def number_of_words
43
- addr = self.address_of_entries - 2
44
- return @memory.force_readw(addr)
45
- end
46
-
47
- def word_address(index)
48
- addr = address_of_entries
49
- addr += entry_length * index
50
-
51
- return addr
52
- end
53
-
54
- def word(index)
55
- addr = word_address(index)
56
-
57
- codes = nil
58
-
59
- if @header.version <= 3
60
- codes = @memory.force_readzstr(addr, 4)[1]
61
- else
62
- codes = @memory.force_readzstr(addr, 6)[1]
63
- end
64
-
65
- str = ZSCII.translate(0, @header.version, codes, @abbreviation_table)
66
- @lookup_cache[str] = index
67
- return str
68
- end
69
-
70
- def lookup_word(token)
71
- if token.length > 6 and @header.version <= 3
72
- token = token[0..5]
73
- elsif token.length > 9
74
- token = token[0..8]
75
- end
76
- cached_index = @lookup_cache[token]
77
- if cached_index != nil
78
- if self.word(cached_index) == token
79
- return cached_index
80
- end
81
- end
82
-
83
- number_of_words.times do |i|
84
- if word(i) == token
85
- cached_index = i
86
- break
87
- end
88
- end
89
-
90
- cached_index
91
- end
92
-
93
- def word_separators
94
- addr = self.address
95
-
96
- # the number of word separators
97
- num_input_codes = @memory.force_readb(addr)
98
- addr += 1
99
-
100
- codes = []
101
- num_input_codes.times do
102
- codes << @memory.force_readb(addr)
103
- addr += 1
104
- end
105
-
106
- codes = codes.map do |i|
107
- ZSCII.translate_Zchar(i)
108
- end
109
-
110
- return codes
111
- end
112
-
113
- def tokenize(line)
114
- orig_line = line
115
-
116
- # I. ensure that word separators are surrounded by spaces
117
- self.word_separators.each do |sep|
118
- line = line.gsub(sep, " " + sep + " ")
119
- end
120
-
121
- # II. break up the line into words delimited by spaces
122
- tokens = line.split(' ')
123
-
124
- line = orig_line
125
- last_pos = 0
126
- ret = tokens.map do |token|
127
- index = line.index(token)
128
- line = line[index+token.size..-1]
129
-
130
- index = index + last_pos
131
- last_pos = index + token.size
132
-
133
- index
134
- { :position => index, :string => token }
135
- end
136
-
137
- ret
138
- end
139
-
140
- def parse(tokens)
141
- ret = []
142
- tokens.each do |token|
143
- index = lookup_word(token[:string])
144
- if index != nil
145
- addr = word_address(index)
146
- else
147
- addr = 0
148
- end
149
- ret << { :size => token[:string].size, :address => addr, :position => token[:position] }
150
- end
151
-
152
- ret
153
- end
154
- end
155
- end
12
+ module Z
13
+ class Dictionary
14
+ def initialize(memory)
15
+ @memory = memory
16
+ @header = Header.new(@memory.contents)
17
+
18
+ @abbreviation_table = AbbreviationTable.new(@memory)
19
+
20
+ @lookup_cache = {}
21
+ end
22
+
23
+ def address
24
+ @header.dictionary_addr
25
+ end
26
+
27
+ def address_of_entries
28
+ addr = self.address
29
+ num_input_codes = @memory.force_readb(addr)
30
+ addr += 1
31
+ addr += num_input_codes
32
+ addr += 3
33
+
34
+ addr
35
+ end
36
+
37
+ def entry_length
38
+ addr = self.address_of_entries - 3
39
+ return @memory.force_readb(addr)
40
+ end
41
+
42
+ def number_of_words
43
+ addr = self.address_of_entries - 2
44
+ return @memory.force_readw(addr)
45
+ end
46
+
47
+ def word_address(index)
48
+ addr = address_of_entries
49
+ addr += entry_length * index
50
+
51
+ return addr
52
+ end
53
+
54
+ def word(index)
55
+ addr = word_address(index)
56
+
57
+ codes = nil
58
+
59
+ if @header.version <= 3
60
+ codes = @memory.force_readzstr(addr, 4)[1]
61
+ else
62
+ codes = @memory.force_readzstr(addr, 6)[1]
63
+ end
64
+
65
+ str = ZSCII.translate(0, @header.version, codes, @abbreviation_table)
66
+ @lookup_cache[str] = index
67
+ return str
68
+ end
69
+
70
+ def lookup_word(token)
71
+ if token.length > 6 and @header.version <= 3
72
+ token = token[0..5]
73
+ elsif token.length > 9
74
+ token = token[0..8]
75
+ end
76
+ cached_index = @lookup_cache[token]
77
+ if cached_index != nil
78
+ if self.word(cached_index) == token
79
+ return cached_index
80
+ end
81
+ end
82
+
83
+ number_of_words.times do |i|
84
+ if word(i) == token
85
+ cached_index = i
86
+ break
87
+ end
88
+ end
89
+
90
+ cached_index
91
+ end
92
+
93
+ def word_separators
94
+ addr = self.address
95
+
96
+ # the number of word separators
97
+ num_input_codes = @memory.force_readb(addr)
98
+ addr += 1
99
+
100
+ codes = []
101
+ num_input_codes.times do
102
+ codes << @memory.force_readb(addr)
103
+ addr += 1
104
+ end
105
+
106
+ codes = codes.map do |i|
107
+ ZSCII.translate_Zchar(i)
108
+ end
109
+
110
+ return codes
111
+ end
112
+
113
+ def tokenize(line)
114
+ orig_line = line
115
+
116
+ # I. ensure that word separators are surrounded by spaces
117
+ self.word_separators.each do |sep|
118
+ line = line.gsub(sep, " " + sep + " ")
119
+ end
120
+
121
+ # II. break up the line into words delimited by spaces
122
+ tokens = line.split(' ')
123
+
124
+ line = orig_line
125
+ last_pos = 0
126
+ ret = tokens.map do |token|
127
+ index = line.index(token)
128
+ line = line[index+token.size..-1]
129
+
130
+ index = index + last_pos
131
+ last_pos = index + token.size
132
+
133
+ index
134
+ { :position => index, :string => token }
135
+ end
136
+
137
+ ret
138
+ end
139
+
140
+ def parse(tokens)
141
+ ret = []
142
+ tokens.each do |token|
143
+ index = lookup_word(token[:string])
144
+ if index != nil
145
+ addr = word_address(index)
146
+ else
147
+ addr = 0
148
+ end
149
+ ret << { :size => token[:string].size, :address => addr, :position => token[:position] }
150
+ end
151
+
152
+ ret
153
+ end
154
+ end
155
+ end
156
156
  end
@@ -1,46 +1,46 @@
1
1
  require 'bit-struct'
2
2
 
3
3
  module Gruesome
4
- module Z
4
+ module Z
5
5
 
6
- # Z-Story File Header
7
- class Header < BitStruct
8
- default_options :endian => :big
6
+ # Z-Story File Header
7
+ class Header < BitStruct
8
+ default_options :endian => :big
9
9
 
10
- unsigned :version, 8, "Z-Machine Version"
11
- unsigned :availablity_flags, 8, "Availability Flags"
12
- unsigned :reservedw1, 16, "Reserved Word 1"
13
- unsigned :high_mem_base, 16, "High Memory Base"
14
- unsigned :entry, 16, "Entry Point"
15
- unsigned :dictionary_addr, 16, "Address of Dictionary"
16
- unsigned :object_tbl_addr, 16, "Address of Object Table"
17
- unsigned :global_var_addr, 16, "Address of Global Variables Table"
18
- unsigned :static_mem_base, 16, "Static Memory Base"
19
- unsigned :capability_flags, 8, "Desired Capability Flags"
20
- unsigned :reservedb1, 8, "Reserved Byte 1"
21
- unsigned :reservedw2, 16, "Reserved Word 2"
22
- unsigned :reservedw3, 16, "Reserved Word 3"
23
- unsigned :reservedw4, 16, "Reserved Word 4"
24
- unsigned :abbrev_tbl_addr, 16, "Address of Abbreviations Table"
25
- unsigned :file_length, 16, "Length of File"
26
- unsigned :checksum, 16, "Checksum of File"
27
- unsigned :interpreter_number, 8, "Interpreter Number"
28
- unsigned :interpreter_version, 8, "Interpreter Version"
29
- unsigned :screen_height, 8, "Screen Height (in Lines)"
30
- unsigned :screen_width, 8, "Screen Height (in Characters)"
31
- unsigned :screen_width_units, 16, "Screen Width (in Units)"
32
- unsigned :screen_height_units, 16, "Screen Height (in Units)"
33
- unsigned :font_width, 8, "Font Width (in Units)"
34
- unsigned :font_height, 8, "Font Height (in Units)"
35
- unsigned :routines_addr, 16, "Offset to Routines (Divided by 8)"
36
- unsigned :static_str_addr, 16, "Offset to Static Strings (Divided by 8)"
37
- unsigned :background_color, 8, "Default Background Color"
38
- unsigned :foreground_color, 8, "Default Foreground Color"
39
- unsigned :terminating_chars_tbl_addr, 16, "Address of Terminating Characters Table"
40
- unsigned :output_stream_3_width, 16, "Total Width of Characters Send to Output Stream 3"
41
- unsigned :revision_number, 16, "Standard Revision Number"
42
- unsigned :alphabet_tbl_addr, 16, "Address of Alphabet Table"
43
- unsigned :header_ext_tbl_addr, 16, "Address of Header Extension Table"
44
- end
45
- end
10
+ unsigned :version, 8, "Z-Machine Version"
11
+ unsigned :availablity_flags, 8, "Availability Flags"
12
+ unsigned :reservedw1, 16, "Reserved Word 1"
13
+ unsigned :high_mem_base, 16, "High Memory Base"
14
+ unsigned :entry, 16, "Entry Point"
15
+ unsigned :dictionary_addr, 16, "Address of Dictionary"
16
+ unsigned :object_tbl_addr, 16, "Address of Object Table"
17
+ unsigned :global_var_addr, 16, "Address of Global Variables Table"
18
+ unsigned :static_mem_base, 16, "Static Memory Base"
19
+ unsigned :capability_flags, 8, "Desired Capability Flags"
20
+ unsigned :reservedb1, 8, "Reserved Byte 1"
21
+ unsigned :reservedw2, 16, "Reserved Word 2"
22
+ unsigned :reservedw3, 16, "Reserved Word 3"
23
+ unsigned :reservedw4, 16, "Reserved Word 4"
24
+ unsigned :abbrev_tbl_addr, 16, "Address of Abbreviations Table"
25
+ unsigned :file_length, 16, "Length of File"
26
+ unsigned :checksum, 16, "Checksum of File"
27
+ unsigned :interpreter_number, 8, "Interpreter Number"
28
+ unsigned :interpreter_version, 8, "Interpreter Version"
29
+ unsigned :screen_height, 8, "Screen Height (in Lines)"
30
+ unsigned :screen_width, 8, "Screen Height (in Characters)"
31
+ unsigned :screen_width_units, 16, "Screen Width (in Units)"
32
+ unsigned :screen_height_units, 16, "Screen Height (in Units)"
33
+ unsigned :font_width, 8, "Font Width (in Units)"
34
+ unsigned :font_height, 8, "Font Height (in Units)"
35
+ unsigned :routines_addr, 16, "Offset to Routines (Divided by 8)"
36
+ unsigned :static_str_addr, 16, "Offset to Static Strings (Divided by 8)"
37
+ unsigned :background_color, 8, "Default Background Color"
38
+ unsigned :foreground_color, 8, "Default Foreground Color"
39
+ unsigned :terminating_chars_tbl_addr, 16, "Address of Terminating Characters Table"
40
+ unsigned :output_stream_3_width, 16, "Total Width of Characters Send to Output Stream 3"
41
+ unsigned :revision_number, 16, "Standard Revision Number"
42
+ unsigned :alphabet_tbl_addr, 16, "Address of Alphabet Table"
43
+ unsigned :header_ext_tbl_addr, 16, "Address of Header Extension Table"
44
+ end
45
+ end
46
46
  end
@@ -1,50 +1,50 @@
1
1
  module Gruesome
2
- module Z
2
+ module Z
3
3
 
4
- # A Z-Machine instruction
5
- class Instruction
6
- attr_reader :opcode # the opcode
7
- attr_reader :types # the types of the operands
8
- attr_reader :operands # the operands given to the instruction
9
- attr_reader :destination # the destination variable to place the result
10
- attr_reader :branch_to # the address to set the pc when branch is taken
11
- # also... if 0, return true from routine
12
- # if 1, return false from routine
13
- attr_reader :branch_on # the condition is matched against this
14
- attr_reader :length # instruction size in number of bytes
4
+ # A Z-Machine instruction
5
+ class Instruction
6
+ attr_reader :opcode # the opcode
7
+ attr_reader :types # the types of the operands
8
+ attr_reader :operands # the operands given to the instruction
9
+ attr_reader :destination # the destination variable to place the result
10
+ attr_reader :branch_to # the address to set the pc when branch is taken
11
+ # also... if 0, return true from routine
12
+ # if 1, return false from routine
13
+ attr_reader :branch_on # the condition is matched against this
14
+ attr_reader :length # instruction size in number of bytes
15
15
 
16
- def initialize(opcode, types, operands, destination, branch_destination, branch_condition, length)
17
- @opcode = opcode
18
- @types = types
19
- @operands = operands
20
- @destination = destination
21
- @branch_to = branch_destination
22
- @branch_on = branch_condition
23
- @length = length
24
- end
16
+ def initialize(opcode, types, operands, destination, branch_destination, branch_condition, length)
17
+ @opcode = opcode
18
+ @types = types
19
+ @operands = operands
20
+ @destination = destination
21
+ @branch_to = branch_destination
22
+ @branch_on = branch_condition
23
+ @length = length
24
+ end
25
25
 
26
- def to_s(version)
27
- line = Opcode.name(@opcode, version)
28
- idx = -1
29
- line = line + @operands.inject("") do |result, element|
30
- idx += 1
31
- if @types[idx] == OperandType::VARIABLE
32
- result + " %" + sprintf("%02x", element)
33
- else
34
- result + " " + element.to_s
35
- end
36
- end
26
+ def to_s(version)
27
+ line = Opcode.name(@opcode, version)
28
+ idx = -1
29
+ line = line + @operands.inject("") do |result, element|
30
+ idx += 1
31
+ if @types[idx] == OperandType::VARIABLE
32
+ result + " %" + sprintf("%02x", element)
33
+ else
34
+ result + " " + element.to_s
35
+ end
36
+ end
37
37
 
38
- if @destination != nil
39
- line = line + " -> %" + sprintf("%02x", @destination)
40
- end
38
+ if @destination != nil
39
+ line = line + " -> %" + sprintf("%02x", @destination)
40
+ end
41
41
 
42
- if @branch_to != nil
43
- line = line + " goto $" + sprintf("%04x", @branch_to) + " on " + @branch_on.to_s
44
- end
42
+ if @branch_to != nil
43
+ line = line + " goto $" + sprintf("%04x", @branch_to) + " on " + @branch_on.to_s
44
+ end
45
45
 
46
- line
47
- end
48
- end
49
- end
46
+ line
47
+ end
48
+ end
49
+ end
50
50
  end
@@ -6,66 +6,67 @@ require_relative 'abbreviation_table'
6
6
  require_relative 'object_table'
7
7
 
8
8
  module Gruesome
9
- module Z
9
+ module Z
10
10
 
11
- # The class that initializes and maintains a Z-Machine
12
- class Machine
11
+ # The class that initializes and maintains a Z-Machine
12
+ class Machine
13
13
 
14
- # Will create a new virtual machine for the game file
15
- def initialize(game_file)
16
- file = File.open(game_file, "r")
14
+ # Will create a new virtual machine for the game file
15
+ def initialize(game_file)
16
+ file = File.open(game_file, "rb")
17
17
 
18
- # I. Create memory space
18
+ # I. Create memory space
19
19
 
20
- memory_size = File.size(game_file)
21
- @memory = Memory.new(file.read(memory_size))
20
+ memory_size = File.size(game_file)
21
+ save_file_name = File.basename(file, File.extname(file)) + ".sav"
22
+ @memory = Memory.new(file.read(memory_size), save_file_name)
22
23
 
23
- # Set flags
24
- flags = @memory.force_readb(0x01)
25
- flags &= ~(0b1110000)
26
- @memory.force_writeb(0x01, flags)
24
+ # Set flags
25
+ flags = @memory.force_readb(0x01)
26
+ flags &= ~(0b1110000)
27
+ @memory.force_writeb(0x01, flags)
27
28
 
28
- # Set flags 2
29
- flags = @memory.force_readb(0x10)
30
- flags &= ~(0b11111100)
31
- @memory.force_writeb(0x10, flags)
29
+ # Set flags 2
30
+ flags = @memory.force_readb(0x10)
31
+ flags &= ~(0b11111100)
32
+ @memory.force_writeb(0x10, flags)
32
33
 
33
- # II. Read header (at address 0x0000) and associated tables
34
- @header = Header.new(@memory.contents)
35
- @object_table = ObjectTable.new(@memory)
34
+ # II. Read header (at address 0x0000) and associated tables
35
+ @header = Header.new(@memory.contents)
36
+ @object_table = ObjectTable.new(@memory)
36
37
 
37
- # III. Instantiate CPU
38
- @decoder = Decoder.new(@memory)
39
- @processor = Processor.new(@memory)
40
- end
38
+ # III. Instantiate CPU
39
+ @decoder = Decoder.new(@memory)
40
+ @processor = Processor.new(@memory)
41
+ end
41
42
 
42
- def execute
43
- while true do
44
- i = @decoder.fetch
45
- #var = @memory.readv(0)
46
- #if var != nil
47
- # puts "var %00 = " + sprintf("%04x", var)
48
- # @memory.writev(0, var)
49
- #end
50
- #var = @memory.readv(1)
51
- #if var != nil
52
- # puts "var %01 = " + sprintf("%04x", @memory.readv(0x01))
53
- #end
54
- #puts "at $" + sprintf("%04x", @memory.program_counter) + ": " + i.to_s(@header.version)
55
- @memory.program_counter += i.length
43
+ def execute
44
+ while true do
45
+ i = @decoder.fetch
46
+ # var = @memory.readv(0)
47
+ # if var != nil
48
+ # puts "var %00 = " + sprintf("%04x", var)
49
+ # @memory.writev(0, var)
50
+ # end
51
+ # var = @memory.readv(1)
52
+ # if var != nil
53
+ # puts "var %01 = " + sprintf("%04x", @memory.readv(0x01))
54
+ # end
55
+ # puts "at $" + sprintf("%04x", @memory.program_counter) + ": " + i.to_s(@header.version)
56
+ @memory.program_counter += i.length
56
57
 
57
- if i.opcode == Opcode::QUIT
58
- break
59
- end
58
+ if i.opcode == Opcode::QUIT
59
+ break
60
+ end
60
61
 
61
- begin
62
- @processor.execute(i)
63
- rescue RuntimeError => fuh
64
- "error at $" + sprintf("%04x", @memory.program_counter) + ": " + i.to_s(@header.version)
65
- end
66
- end
67
- end
68
- end
69
- end
62
+ begin
63
+ @processor.execute(i)
64
+ rescue RuntimeError => fuh
65
+ puts "error at $" + sprintf("%04x", @memory.program_counter) + ": " + i.to_s(@header.version)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
70
71
  end
71
72