test65 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fb249a06581fa2e69553600dc88ec52b620db6d5
4
+ data.tar.gz: cdbc6072308051d64c07c61e0c9d07ded8319b2e
5
+ SHA512:
6
+ metadata.gz: cca5d442da99eb33a2fcc40f40029e4981e03fddeb037cb7b1e8b5123235b2725e0bc40a4e9927e9bb394827535048b9e5ad7d93ad360e25f99cfba2517ddc59
7
+ data.tar.gz: fdb97d5bbe8f9c7671904ab4591749740e2860d544416a04e269d13a5caac869da6c3173997d7b756b2db21b6ec0ff2ccd8cfed7fd75a7834b164514e2c5fd01
@@ -0,0 +1,5 @@
1
+ *.out
2
+ *.o
3
+ *.lst
4
+ pkg
5
+ foo
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at peter.c.camilleri@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in test65.gemspec
6
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 PeterCamilleri
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.
@@ -0,0 +1,159 @@
1
+ # Test65
2
+
3
+ This is a simple program written to allow the testing of 65C02 code that
4
+ I am writing for the Commander X 16 neo-retro computer.
5
+
6
+ Note: This program has recently undergone a lot of upgrades and is now ready
7
+ to release as a Ruby gem.
8
+
9
+ ## Installation
10
+
11
+ To install, first install the cc65 compiler and ensure that it is available at
12
+ the command line. You can test the required components with:
13
+
14
+ ca65 --version
15
+ ld65 --version
16
+ sim65 --version
17
+ set CC65_HOME
18
+
19
+ Then to install the gem, use:
20
+
21
+ $ gem install test65
22
+
23
+ ## Usage
24
+
25
+ test65 - Run tests of 65c02 assembler code.
26
+
27
+ Usage: test65 {options} {files}
28
+
29
+ Options:
30
+ --debug, -d Show (really) detailed debug info. Not normally required.
31
+ --help, -h, -? Display this message and exit.
32
+ --list, -l Generate list files for each test.
33
+ --map, -m Generate map files for each test.
34
+ --keep, -k Keep object and output files. Do not clean up.
35
+ --path, -p Specify the path to the test files. (Only 1 allowed)
36
+ --quiet, -q Hide messages. Used to keep the test65 tests clean.
37
+ --verbose, -v Display lots of useful progress info.
38
+ --version Display the program version and exit.
39
+
40
+ Files: An optional list of test files.
41
+
42
+ Notes:
43
+ - By default, test files are located in a folder called "t65" in the current
44
+ folder or one of its parent folders.
45
+ - Wildcards are allowed for files but not the path.
46
+ - If no files are specified, then all files matching "t65*.a65" or "t65*.rb"
47
+ in the test folder are tested.
48
+ - If files are specified, only the specified file(s) are tested.
49
+
50
+ ## Test Files
51
+
52
+ The test65 utility finds test files in one of two ways:
53
+
54
+ 1. The test files can be listed explicitly on the command line.
55
+ 2. Test files, of the appropriate extension, located in the test folder, are
56
+ "batch" run.
57
+
58
+ The tests are located in a test folder. This can be:
59
+
60
+ 1. Explicitly named using the --path option. (See above)
61
+ 2. Located in the t65 folder, either off of the current folder, or one of
62
+ its parent folders.
63
+
64
+ Test files can be:
65
+
66
+ 1. Assembly files with a suffix of ".a65".
67
+ 2. Ruby script files with a suffix of ".rb".
68
+
69
+ Future versions may support alternate extension types for assembler (like
70
+ ".asm" for example) and tests written in the "C" language with extensions
71
+ of ".c65" and maybe ".c".
72
+
73
+ ## Test Scripts in Ruby
74
+
75
+ The test65 gem uses Ruby scripts to allow for cases not handled by the
76
+ conventional defaults. The basic form of these scripts is:
77
+
78
+ ```
79
+ # Minimum script to pass a test.
80
+
81
+ Test65.script do
82
+ ca65 "min_pass.a65"
83
+ ld65
84
+ sim65
85
+ end
86
+ ```
87
+
88
+ The order matters and is:
89
+
90
+ assembly and compiling then linking then simulation
91
+
92
+ If the order is wrong, there will be errors.
93
+
94
+ ### Assembly
95
+
96
+ Code is assembled using the _ca65_ statement. It is supported by the
97
+ _ca65\_inc\_paths_ method. They are:
98
+
99
+ ```
100
+ ca65_inc_paths <path>, <optional more paths>, <etc>, <etc>, <etc>
101
+ ca65 "source_file", "optional options"
102
+ ```
103
+
104
+ Where _ca65\_inc\_paths_ is used to tell the assembler where to find included
105
+ files and _ca65_ does the work of converting the source file to an
106
+ object file.
107
+
108
+ ### Linking
109
+
110
+ Code is linked using the _ld65_ statement. It is supported by the
111
+ _ld65\_lib\_paths_ and _ld65\_libraries_. Here they are:
112
+
113
+ ```
114
+ ld65_lib_paths "path to libraries", <etc>, <etc>, <etc>
115
+ ld65_libraries "library", <etc>, <etc>, <etc>
116
+ ld65 "optional options"
117
+ ```
118
+
119
+ Where _ld65\_lib\_paths_ tells the linker where to find library files and
120
+ _ld65\_libraries_ specifies those libraries. After any ld65 method, no further
121
+ ca65* methods are permitted. After _ld65_ no further ld65* methods are
122
+ permitted either.
123
+
124
+ ### Simulation
125
+
126
+ Finally, the simulation phase runs the actual tests. This is simply:
127
+
128
+ ```
129
+ sim65 "optional options"
130
+ ```
131
+
132
+ When this is done, no further test statements are permitted. However it is
133
+ possible to perform multiple tests in one script file. Simply code
134
+ multiple Test65.script { ... } blocks in the one Ruby script file. They
135
+ will be run one after another, in order.
136
+
137
+ ## Contributing
138
+ 1. Fork it
139
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
140
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
141
+ 4. Push to the branch (`git push origin my-new-feature`)
142
+ 5. Create new Pull Request
143
+
144
+ OR...
145
+
146
+ * Make a suggestion by raising an
147
+ [issue](https://github.com/PeterCamilleri/test65/issues)
148
+ . All ideas and comments are welcome.
149
+
150
+ ## License
151
+
152
+ The gem is available as open source under the terms of the
153
+ [MIT License](./LICENSE.txt).
154
+
155
+ ## Code of Conduct
156
+
157
+ Everyone interacting in the test65project’s codebases, issue trackers,
158
+ chat rooms and mailing lists is expected to follow the
159
+ [code of conduct](./CODE_OF_CONDUCT.md).
@@ -0,0 +1,87 @@
1
+ ; Macro definitions for testing 65c02 code using sim65.
2
+
3
+ .import exit:absolute ; Make sure we have this symbol defined.
4
+
5
+ ; Exit tests and return success!
6
+ .macro tests_pass
7
+ lda #0
8
+ jmp exit
9
+ .endmacro
10
+
11
+ ; Exit tests and return an error code :-(
12
+ .macro tests_fail err_code
13
+ .ifconst err_code
14
+ .if (err_code < 2) || (err_code > 255)
15
+ .fatal "Invalid error code. Allowed values 2..255"
16
+ .endif
17
+
18
+ lda #err_code
19
+ jmp exit
20
+ .else
21
+ .fatal "Error code must be a constant"
22
+ .endif
23
+ .endmacro
24
+
25
+ ; Exit tests and return an error code if the Z flag is cleared (ne)
26
+ .macro fail_ne err_code
27
+ .local no_error
28
+ beq no_error
29
+ tests_fail err_code
30
+ no_error:
31
+ .endmacro
32
+
33
+ ; Exit tests and return an error code if the Z flag is set (eq)
34
+ .macro fail_eq err_code
35
+ .local no_error
36
+ bne no_error
37
+ tests_fail err_code
38
+ no_error:
39
+ .endmacro
40
+
41
+ ; Exit tests and return an error code if the C flag is cleared (cc)
42
+ .macro fail_cc err_code
43
+ .local no_error
44
+ bcs no_error
45
+ tests_fail err_code
46
+ no_error:
47
+ .endmacro
48
+
49
+ ; Exit tests and return an error code if the C flag is set (cs)
50
+ .macro fail_cs err_code
51
+ .local no_error
52
+ bcc no_error
53
+ tests_fail err_code
54
+ no_error:
55
+ .endmacro
56
+
57
+ ; Exit tests and return an error code if the N flag is cleared (pl)
58
+ .macro fail_pl err_code
59
+ .local no_error
60
+ bmi no_error
61
+ tests_fail err_code
62
+ no_error:
63
+ .endmacro
64
+
65
+ ; Exit tests and return an error code if the N flag is set (mi)
66
+ .macro fail_mi err_code
67
+ .local no_error
68
+ bpl no_error
69
+ tests_fail err_code
70
+ no_error:
71
+ .endmacro
72
+
73
+ ; Exit tests and return an error code if the V flag is cleared (vc)
74
+ .macro fail_vc err_code
75
+ .local no_error
76
+ bvs no_error
77
+ tests_fail err_code
78
+ no_error:
79
+ .endmacro
80
+
81
+ ; Exit tests and return an error code if the V flag is set (vs)
82
+ .macro fail_vs err_code
83
+ .local no_error
84
+ bvc no_error
85
+ tests_fail err_code
86
+ no_error:
87
+ .endmacro
@@ -0,0 +1,37 @@
1
+ SYMBOLS {
2
+ __EXEHDR__: type = import;
3
+ __STACKSIZE__: type = weak, value = $0800; # 2k stack
4
+ }
5
+ MEMORY {
6
+ ZP: file = "", start = $0000, size = $0100;
7
+ HEADER: file = %O, start = $0000, size = $000C;
8
+ MAIN: file = %O, define = yes, start = $0200, size = $FDF0 - __STACKSIZE__;
9
+ }
10
+ SEGMENTS {
11
+ ZEROPAGE: load = ZP, type = zp;
12
+ EXEHDR: load = HEADER, type = ro;
13
+ STARTUP: load = MAIN, type = ro;
14
+ LOWCODE: load = MAIN, type = ro, optional = yes;
15
+ ONCE: load = MAIN, type = ro, optional = yes;
16
+ CODE: load = MAIN, type = ro;
17
+ RODATA: load = MAIN, type = ro;
18
+ PAGE_CODE: load = MAIN, type = ro, optional = yes, align = $100;
19
+ DATA: load = MAIN, type = rw;
20
+ PAGE_DATA: load = MAIN, type = rw, optional = yes, align = $100;
21
+ BSS: load = MAIN, type = bss, define = yes;
22
+ }
23
+ FEATURES {
24
+ CONDES: type = constructor,
25
+ label = __CONSTRUCTOR_TABLE__,
26
+ count = __CONSTRUCTOR_COUNT__,
27
+ segment = ONCE;
28
+ CONDES: type = destructor,
29
+ label = __DESTRUCTOR_TABLE__,
30
+ count = __DESTRUCTOR_COUNT__,
31
+ segment = RODATA;
32
+ CONDES: type = interruptor,
33
+ label = __INTERRUPTOR_TABLE__,
34
+ count = __INTERRUPTOR_COUNT__,
35
+ segment = RODATA,
36
+ import = __CALLIRQ__;
37
+ }
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ #The test65 command line script.
3
+
4
+ require_relative '../lib/test65'
5
+
6
+ Test65.process
@@ -0,0 +1,24 @@
1
+ test65 - Run tests of 65c02 assembler code.
2
+
3
+ Usage: test65 {options} {files}
4
+
5
+ Options:
6
+ --debug, -d Show (really) detailed debug info. Not normally required.
7
+ --help, -h, -? Display this message and exit.
8
+ --list, -l Generate list files for each test.
9
+ --map, -m Generate map files for each test.
10
+ --keep, -k Keep object and output files. Do not clean up.
11
+ --path, -p Specify the path to the test files. (Only 1 allowed)
12
+ --quiet, -q Hide messages. Used to keep the test65 tests clean.
13
+ --verbose, -v Display lots of useful progress info.
14
+ --version Display the program version and exit.
15
+
16
+ Files: An optional list of test files.
17
+
18
+ Notes:
19
+ - By default, test files are located in a folder called "t65" in the current
20
+ folder or one of its parent folders.
21
+ - Wildcards are allowed for files but not the path.
22
+ - If no files are specified, then all files matching "t65*.a65" or "t65*.rb"
23
+ in the test folder are tested.
24
+ - If files are specified, only the specified file(s) are tested.
@@ -0,0 +1,50 @@
1
+ # test65
2
+
3
+ require 'english'
4
+ require 'getoptlong'
5
+ require 'pathname'
6
+ require 'rbconfig'
7
+ require 'full_dup'
8
+
9
+ require_relative 'test65/perform_test'
10
+ require_relative 'test65/process_files'
11
+ require_relative 'test65/process_args'
12
+ require_relative 'test65/process_path'
13
+ require_relative 'test65/build_file_list'
14
+ require_relative 'test65/std_path'
15
+ require_relative 'test65/host'
16
+ require_relative 'test65/version'
17
+
18
+ module Test65
19
+
20
+ #Figure out where the gem root folder is located
21
+ lib_path = File.dirname(File.absolute_path(__FILE__))
22
+ @gem_root = Pathname.new(lib_path).parent.to_s
23
+ @asminc = @gem_root + "/asminc" # TODO remove this variable.
24
+
25
+ # Figure out where the compiler is.
26
+ temp = ENV["CC65_HOME"]
27
+ fail "The CC65_HOME variable is not set." unless temp
28
+ @cc65_home = temp.standardize_path
29
+
30
+ # The code entry point. Run some 65c02 tests.
31
+ # Returns
32
+ # 0 for success
33
+ # Other for failure
34
+ def self.process
35
+ process_args
36
+ process_path
37
+ build_file_list
38
+ process_file_list
39
+
40
+ rescue => err
41
+ puts "Error: #{err.to_s}"
42
+ puts err.backtrace if @options[:debug]
43
+ exit(1)
44
+
45
+ ensure
46
+ # Always remove this file if it exists.
47
+ File.delete("_kwhyit") if File.exists?("_kwhyit")
48
+ end
49
+
50
+ end
@@ -0,0 +1,37 @@
1
+ # Build the list of test and library files to be processed.
2
+
3
+ module Test65
4
+ # Find the files to be tested.
5
+ def self.build_file_list
6
+ if @arg_files.empty?
7
+ scan_files
8
+ else
9
+ check_files
10
+ end
11
+
12
+ puts "Processing #{@test_files.length} test file(s)" if @options[:verbose]
13
+ end
14
+
15
+ # Scan the path for files to be processed.
16
+ def self.scan_files
17
+ @test_files = Dir.glob(@options[:path] + "/t65*.a65") +
18
+ Dir.glob(@options[:path] + "/t65*.rb")
19
+ fail "Cannot locate any test files" if @test_files.empty?
20
+ end
21
+
22
+ # Check the list of files to be processed.
23
+ def self.check_files
24
+ @test_files = @arg_files.map do |file|
25
+ std_file = file.standardize_path
26
+
27
+ if !(found = Dir.glob(std_file)).empty?
28
+ found.map {|subfile| File.absolute_path(file) }
29
+ elsif !(found = Dir.glob(@options[:path] + "/" + std_file)).empty?
30
+ found
31
+ else
32
+ fail "Cannot locate the file #{file}"
33
+ end
34
+ end.flatten
35
+ end
36
+
37
+ end
@@ -0,0 +1,35 @@
1
+ # An enclosure for the ca65 assembler.
2
+ class TestScript
3
+
4
+ # Setup ca65
5
+ def ca65_initialize
6
+ @options[:target] = "sim65c02"
7
+ @options[:ca65_paths] = ["#{@options[:gem_root]}/asminc"]
8
+ @options[:objs] = []
9
+ end
10
+
11
+ # Add some include paths for the assembler.
12
+ def ca65_inc_paths(*more_paths)
13
+ fail "Sequence error: ca65_inc_paths" unless @phase == :create
14
+ append_option(:inc_paths, more_paths)
15
+ end
16
+
17
+ # Assemble some files.
18
+ def ca65(file, options="")
19
+ fail "Sequence error: ca65" unless @phase == :create
20
+ source = File.absolute_path(@options[:path] + "/" + file)
21
+ target = "--target #{@options[:target]} "
22
+ paths = build_args("-I", @options[:ca65_paths])
23
+
24
+ # Convert source assemble files into object files.
25
+ object = change_type(source, ".o")
26
+ list = @options[:list] ? "-l " + change_type(source, ".lst") : ""
27
+ command = "ca65 #{target}#{paths}#{list}#{options} -o #{object} #{source} #{@quiet}\n"
28
+ puts command if @options[:debug]
29
+ system(command)
30
+ fail "Error assembling #{source.localize_path}" unless $?.exitstatus == 0
31
+
32
+ @options[:objs] << object
33
+ end
34
+
35
+ end
@@ -0,0 +1,46 @@
1
+ # An enclosure for the ld65 linker.
2
+
3
+ class TestScript
4
+
5
+ # Setup ld65
6
+ def ld65_initialize
7
+ @options[:lib_paths] = []
8
+ @options[:libraries] = ["sim65c02.lib"]
9
+ @options[:ld65_options] = []
10
+ @options[:config] = "#{@options[:gem_root]}/cfg/test65.cfg"
11
+ end
12
+
13
+ # Add some library paths for the linker.
14
+ def ld65_lib_paths(*more_paths)
15
+ fail "Sequence error: ld65_lib_paths" unless [:create, :link].include?(@phase)
16
+ append_option(:lib_paths, more_paths)
17
+ @phase = :link
18
+ end
19
+
20
+ # Add some library paths for the linker.
21
+ def ld65_libraries(*more_libs)
22
+ fail "Sequence error: ld65_libraries" unless [:create, :link].include?(@phase)
23
+ append_option(:libraries, more_libs)
24
+ @phase = :link
25
+ end
26
+
27
+ def ld65(options="")
28
+ fail "Sequence error: ld65" unless [:create, :link].include?(@phase)
29
+ @phase = :simulate
30
+
31
+ @output = change_type(@options[:objs][0], ".out")
32
+ @map_file = change_type(@options[:objs][0], ".map")
33
+ lib_paths = build_args("--lib-path", @options[:lib_paths])
34
+ objs = build_args(@options[:objs])
35
+ libs = build_args("--lib", @options[:libraries])
36
+ map = @options[:map] ? "-m #{@map_file}" : ""
37
+ cfg = "-C " + @options[:config] + " "
38
+
39
+ # Build the command and run it.
40
+ command = "ld65 #{lib_paths}#{libs}#{cfg}#{options} #{objs}#{map} -o #{@output} #{@quiet}\n"
41
+ puts command if @options[:debug]
42
+ system(command)
43
+ fail "Error linking #{@output.localize_path}." unless $?.exitstatus == 0
44
+ end
45
+
46
+ end
@@ -0,0 +1,23 @@
1
+ # An enclosure for the sim65 simulator.
2
+
3
+ class TestScript
4
+
5
+ # Setup sim65
6
+ def sim65_initialize
7
+ @options[:sim65_options] = []
8
+ end
9
+
10
+ # Run the simulator to get the actual test results.
11
+ def sim65(options="")
12
+ fail "Sequence error: sim65" unless @phase == :simulate
13
+ @phase = :done
14
+
15
+ # Build the command and run it.
16
+ command = "sim65 #{options} #{@output} #{@quiet}\n"
17
+ puts command if @options[:debug]
18
+ system(command)
19
+ status = $?.exitstatus
20
+ fail "Test #{@output.localize_path} failed with error code: #{status}" unless status == 0
21
+ end
22
+
23
+ end
@@ -0,0 +1,26 @@
1
+ # Utility methods
2
+
3
+ class TestScript
4
+
5
+ # Create a file name with a new extension.
6
+ def change_type(file, new_ext)
7
+ file.gsub(/\...?.?\z/, new_ext)
8
+ end
9
+
10
+ # Construct a series of arguments.
11
+ def build_args(prefix = nil, args)
12
+ pre = prefix ? prefix + " " : ""
13
+ args.inject("") {|str, arg| str << pre + arg + " "}
14
+ end
15
+
16
+ # Append to an option.
17
+ def append_option(key, *value)
18
+ @options[key] = ((@options[key] || []) + [value]).flatten
19
+ end
20
+
21
+ # Adjust a list of files to include the path to the test file.
22
+ def adjust(files)
23
+ files.map { |file| @options[:path] + "/" + file }
24
+ end
25
+
26
+ end
@@ -0,0 +1,13 @@
1
+ # Is this program running on a Windows host?
2
+
3
+ class Object
4
+
5
+ private
6
+
7
+ # Are we running under Windows?
8
+ def windows?
9
+ # Is this a Windows based system
10
+ @win ||= RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|bccwin|wince|emc/
11
+ end
12
+
13
+ end
@@ -0,0 +1,45 @@
1
+ # Run a test script for the test65 program.
2
+
3
+ require_relative 'enclosures/utils'
4
+ require_relative 'enclosures/ca65'
5
+ require_relative 'enclosures/ld65'
6
+ require_relative 'enclosures/sim65'
7
+
8
+ class TestScript
9
+
10
+ # Create a test script object and set up its default options.
11
+ def initialize(options={})
12
+ @options = options.full_dup
13
+ @phase = :create
14
+
15
+ ca65_initialize
16
+ ld65_initialize
17
+ sim65_initialize
18
+
19
+ @quiet = @options[:quiet].to_s
20
+ end
21
+
22
+ # Cleanup after ourselves
23
+ def clean_up
24
+ # Remove objects and executable unless told to keep them.
25
+ unless @options[:keep]
26
+ File.delete(@output) if File.exists?(@output)
27
+
28
+ @options[:objs].each do |file|
29
+ File.delete(file) if File.exists?(file)
30
+ end
31
+ end
32
+
33
+ # Remove listing files unless they were requested.
34
+ unless @options[:list]
35
+ @options[:objs].each do |file|
36
+ file = change_type(file, ".lst")
37
+ File.delete(file) if File.exists?(file)
38
+ end
39
+ end
40
+
41
+ # Remove the map file if needed.
42
+ File.delete(@map_file) if !@options[:map] && File.exists?(@map_file)
43
+ end
44
+
45
+ end
@@ -0,0 +1,66 @@
1
+ # Process the command line arguments for the test65 program.
2
+
3
+ module Test65
4
+ # Process the command line arguments.
5
+ def self.process_args
6
+ @options = {}
7
+
8
+ opts = GetoptLong.new(
9
+ ["--debug", "-d", GetoptLong::NO_ARGUMENT],
10
+ ["--help", "-h", "-?", GetoptLong::NO_ARGUMENT],
11
+ ["--list", "-l", GetoptLong::NO_ARGUMENT],
12
+ ["--map", "-m", GetoptLong::NO_ARGUMENT],
13
+ ["--keep", "-k", GetoptLong::NO_ARGUMENT],
14
+ ["--path", "-p", GetoptLong::REQUIRED_ARGUMENT],
15
+ ["--quiet", "-q", GetoptLong::NO_ARGUMENT],
16
+ ["--verbose", "-v", GetoptLong::NO_ARGUMENT],
17
+ ["--version", GetoptLong::NO_ARGUMENT])
18
+
19
+ opts.each do |opt, arg|
20
+ case opt
21
+ when "--debug"
22
+ @options[:debug] = true
23
+ when "--help"
24
+ puts IO.read(@gem_root + "/help.txt")
25
+ exit
26
+ when "--keep"
27
+ @options[:keep] = true
28
+ when "--list"
29
+ @options[:list] = true
30
+ when "--map"
31
+ @options[:map] = true
32
+ when "--path"
33
+ unless @options[:path]
34
+ @options[:path] = File.absolute_path(arg.standardize_path)
35
+ else
36
+ fail "Multiple path options are not allowed."
37
+ end
38
+ when "--quiet"
39
+ @options[:quiet] = "2> _kwhyit"
40
+ when "--verbose"
41
+ @options[:verbose] = true
42
+ when "--version"
43
+ puts "test65 Version #{VERSION}"
44
+ exit
45
+ else
46
+ fail "Invalid option: #{opt}"
47
+ end
48
+ end
49
+
50
+ # A list of files to test may follow the options.
51
+ @arg_files = ARGV
52
+
53
+ # Setup some default paths
54
+ @options[:gem_root] = @gem_root
55
+ @options[:ca65_paths] = @gem_root + "/asminc"
56
+ @options[:cc65_home] = @cc65_home
57
+
58
+ rescue => err
59
+ puts "Error: #{err.to_s}"
60
+ puts err.backtrace if @options[:debug]
61
+ puts
62
+ puts IO.read(@gem_root + "/help.txt")
63
+ exit
64
+ end
65
+
66
+ end
@@ -0,0 +1,48 @@
1
+ # Process the test files.
2
+
3
+ module Test65
4
+
5
+ # Process the list of files
6
+ def self.process_file_list
7
+ @error_count = 0
8
+
9
+ @test_files.each do |file|
10
+ process_file(file)
11
+ end
12
+
13
+ if @error_count > 0
14
+ fail "#{@error_count} tests failed."
15
+ else
16
+ puts "OK: All tests passed."
17
+ end
18
+ end
19
+
20
+ # Process a file.
21
+ def self.process_file(file)
22
+ puts file.localize_path if @options[:verbose]
23
+
24
+ case File.extname(file)
25
+ when ".a65"
26
+ script { ca65(File.basename(file)); ld65; sim65 }
27
+
28
+ when ".rb"
29
+ load file
30
+
31
+ else
32
+ fail "Don't know how to process #{file}"
33
+ end
34
+ end
35
+
36
+ # Process a test script.
37
+ def self.script(&block)
38
+ test_script = TestScript.new(@options)
39
+ test_script.instance_exec(&block)
40
+ rescue => err
41
+ puts err
42
+ puts err.backtrace if @options[:debug]
43
+ @error_count += 1
44
+ ensure
45
+ test_script.clean_up
46
+ end
47
+
48
+ end
@@ -0,0 +1,36 @@
1
+ # Process the path to the test files.
2
+
3
+ module Test65
4
+
5
+ # Determine the path to test files.
6
+ def self.process_path
7
+ path = @options[:path] || get_default_path
8
+
9
+ localized_path = path.localize_path
10
+ fail "Path #{localized_path} does not exist." unless File.exists?(path)
11
+ fail "Path #{localized_path} is not a folder." unless File.directory?(path)
12
+ puts "Using path: #{localized_path}" if @options[:verbose]
13
+
14
+ @options[:path] = path
15
+ end
16
+
17
+ # Get the default test file path if one was not supplied.
18
+ def self.get_default_path
19
+ search, parent = Pathname.new(Dir.pwd), nil
20
+
21
+ while true
22
+ test = search.to_s + "/t65"
23
+
24
+ if File.exists?(test)
25
+ return test if File.directory?(test)
26
+ fail "The file #{local_path(test)} is not a folder."
27
+ end
28
+
29
+ search, parent = search.parent, search
30
+
31
+ fail "Default path not found." if search == parent
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,15 @@
1
+ # Portable path handling methods for the Test65 program.
2
+
3
+ class String
4
+
5
+ # Convert a path into standard form.
6
+ def standardize_path
7
+ self.gsub("\\", "/")
8
+ end
9
+
10
+ # Convert a path string into local system form.
11
+ def localize_path
12
+ windows? ? self.gsub("/", "\\") : self
13
+ end
14
+
15
+ end
@@ -0,0 +1,5 @@
1
+ module Test65
2
+ VERSION = "0.5.0".freeze
3
+
4
+ DESCRIPTION = "test65: A testing framework for ca65.".freeze
5
+ end
@@ -0,0 +1,16 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
11
+
12
+ desc "What version of test65 is this?"
13
+ task :vers do |t|
14
+ puts
15
+ puts "test65 version = #{::Test65::VERSION}"
16
+ end
@@ -0,0 +1,12 @@
1
+ ; See how we deal with an assembly error.
2
+
3
+ .import exit:absolute
4
+ .export _main
5
+
6
+ .pc02
7
+
8
+ .code
9
+
10
+ _main:
11
+ lad #0 ; Deliberate error
12
+ jmp exit
@@ -0,0 +1,14 @@
1
+ ; Test a script with multiple objects
2
+
3
+ .export add_two:absolute
4
+ .pc02
5
+ .code
6
+
7
+ .ifdef do_add_two
8
+
9
+ add_two:
10
+ clc
11
+ adc #2
12
+ rts
13
+
14
+ .endif
@@ -0,0 +1,17 @@
1
+ ; Test a script with multiple objects
2
+ .include "test65.i65"
3
+
4
+ .import exit:absolute
5
+ .import add_two:absolute
6
+ .export _main
7
+
8
+ .pc02
9
+
10
+ .code
11
+
12
+ _main:
13
+ lda #10
14
+ jsr add_two
15
+ cmp #12
16
+ fail_ne 10
17
+ tests_pass
@@ -0,0 +1,3 @@
1
+ See how we deal with a nonsense file.
2
+ foo foo foo foo foo foo
3
+ bar bar bar bar bar bar
@@ -0,0 +1,13 @@
1
+ ; See how we deal with a linker error.
2
+
3
+ .import exit:absolute
4
+ .import an_error:absolute
5
+ .export _main
6
+
7
+ .pc02
8
+
9
+ .code
10
+
11
+ _main:
12
+ lda an_error
13
+ jmp exit
@@ -0,0 +1,11 @@
1
+ ; Test detecting an invalid err code.
2
+
3
+ .include "test65.i65"
4
+ .export _main
5
+ .pc02
6
+
7
+ .code
8
+
9
+ _main:
10
+ tests_fail 1
11
+ tests_pass
@@ -0,0 +1,12 @@
1
+ ; Test detecting an non-constant err code.
2
+
3
+ .include "test65.i65"
4
+ .export _main
5
+ .import an_error
6
+ .pc02
7
+
8
+ .code
9
+
10
+ _main:
11
+ tests_fail an_error
12
+ tests_pass
@@ -0,0 +1,12 @@
1
+ ; Minimum code that fails a test
2
+
3
+ .import exit:absolute
4
+ .export _main
5
+
6
+ .pc02
7
+
8
+ .code
9
+
10
+ _main:
11
+ lda #$ff
12
+ jmp exit
@@ -0,0 +1,12 @@
1
+ ; Minimum code that passes a test
2
+
3
+ .import exit:absolute
4
+ .export _main
5
+
6
+ .pc02
7
+
8
+ .code
9
+
10
+ _main:
11
+ lda #0
12
+ jmp exit
@@ -0,0 +1,9 @@
1
+ # Minimum script to pass a test.
2
+
3
+ Test65.script do
4
+
5
+ ca65 "min_pass.a65"
6
+ ld65
7
+ sim65
8
+
9
+ end
@@ -0,0 +1,10 @@
1
+ ; Minimum code that passes a test
2
+
3
+ .include "test65.i65"
4
+ .export _main
5
+ .pc02
6
+
7
+ .code
8
+
9
+ _main:
10
+ tests_pass
@@ -0,0 +1,9 @@
1
+ # Minimum script to pass a test.
2
+
3
+ Test65.script do
4
+
5
+ ca65 "min_pass.a65"
6
+ ld65
7
+ sim65
8
+
9
+ end
@@ -0,0 +1,13 @@
1
+ ; Minimum code that passes a test
2
+
3
+ .include "test65.i65"
4
+ .export _main
5
+ .pc02
6
+
7
+ .zeropage
8
+ mem1: .res 32
9
+
10
+ .code
11
+
12
+ _main:
13
+ tests_pass
@@ -0,0 +1,37 @@
1
+ ; Test a whole bunch of macros!
2
+
3
+ .include "test65.i65"
4
+ .export _main
5
+ .pc02
6
+
7
+ .code
8
+
9
+ _main:
10
+ lda #$00
11
+ fail_ne 10
12
+
13
+ inc
14
+ fail_eq 20
15
+ fail_mi 30
16
+
17
+ dec
18
+ dec
19
+ fail_pl 40
20
+
21
+ clc
22
+ adc #$02
23
+ fail_cc 50
24
+
25
+ clc
26
+ adc #$00
27
+ fail_cs 60
28
+
29
+ clv
30
+ fail_vs 70
31
+
32
+ lda #$70
33
+ clc
34
+ adc #$70
35
+ fail_vc 80
36
+
37
+ tests_pass
@@ -0,0 +1,10 @@
1
+ # Test a script with two source files.
2
+
3
+ Test65.script do
4
+
5
+ ca65 "caller.a65"
6
+ ca65 "callee.a65", "-D do_add_two"
7
+ ld65
8
+ sim65
9
+
10
+ end
@@ -0,0 +1,31 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "test65/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "test65"
7
+ spec.version = Test65::VERSION
8
+ spec.authors = ["PeterCamilleri"]
9
+ spec.email = ["peter.c.camilleri@gmail.com"]
10
+
11
+ spec.summary = Test65::DESCRIPTION
12
+ spec.description = "A testing framework for work on the Commander X 16."
13
+ spec.homepage = "https://github.com/PeterCamilleri/test65"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|docs)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec
21
+ .files
22
+ .reject { |f| f.downcase == 'exe/readme.md'}
23
+ .grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_runtime_dependency "full_dup"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.17"
29
+ spec.add_development_dependency "rake", ">= 12.3.3"
30
+ spec.add_development_dependency "minitest", "~> 5.0"
31
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: test65
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - PeterCamilleri
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-06-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: full_dup
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.17'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.17'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 12.3.3
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 12.3.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ description: A testing framework for work on the Commander X 16.
70
+ email:
71
+ - peter.c.camilleri@gmail.com
72
+ executables:
73
+ - test65
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - CODE_OF_CONDUCT.md
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - asminc/test65.i65
83
+ - cfg/test65.cfg
84
+ - exe/test65
85
+ - help.txt
86
+ - lib/test65.rb
87
+ - lib/test65/build_file_list.rb
88
+ - lib/test65/enclosures/ca65.rb
89
+ - lib/test65/enclosures/ld65.rb
90
+ - lib/test65/enclosures/sim65.rb
91
+ - lib/test65/enclosures/utils.rb
92
+ - lib/test65/host.rb
93
+ - lib/test65/perform_test.rb
94
+ - lib/test65/process_args.rb
95
+ - lib/test65/process_files.rb
96
+ - lib/test65/process_path.rb
97
+ - lib/test65/std_path.rb
98
+ - lib/test65/version.rb
99
+ - rakefile.rb
100
+ - t65/asm_err.a65
101
+ - t65/callee.a65
102
+ - t65/caller.a65
103
+ - t65/foo.txt
104
+ - t65/linker_err.a65
105
+ - t65/macro_err1.a65
106
+ - t65/macro_err2.a65
107
+ - t65/min_fail.a65
108
+ - t65/min_pass.a65
109
+ - t65/min_pass.rb
110
+ - t65/pass.a65
111
+ - t65/t65_min_pass.rb
112
+ - t65/t65_test_cfg.a65
113
+ - t65/t65_test_macros.a65
114
+ - t65/t65_two_files.rb
115
+ - test65.gemspec
116
+ homepage: https://github.com/PeterCamilleri/test65
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.5.2
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: 'test65: A testing framework for ca65.'
140
+ test_files: []