asm6502 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/README.md +176 -0
- data/asm6502.gemspec +9 -0
- data/lib/asm6502.rb +97 -0
- data/lib/asm6502/vcs.rb +2 -0
- data/lib/asm6502/vcs/macro.rb +157 -0
- data/lib/asm6502/vcs/vcs.rb +91 -0
- metadata +48 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b930f2636eba5307b21d5b2c4ae91da53900323ba7014b79e654ca788d16983c
|
4
|
+
data.tar.gz: c51fe89160ee92824ad11da37bc79abeeb0bb03a6edcae861a004a07780b89aa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: aacc404e4bb1eead3d9388f4b25db223e44276c1abd8f1cd2beb7b10e684025617c2c64085c52ca7b6c0114f617d45fa26a5455c1b3b46a29cf609bba2f05a6e
|
7
|
+
data.tar.gz: cdef1cfea165a0c96b07ea4ca8f337d0b81ed459cb38a5a8f87ed0c20517093dda8241c044348cd24c9f0bf6456be3fd688c8c40cce02ed3ed81b60983fec13e
|
data/.gitignore
ADDED
data/README.md
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
# asm6502
|
2
|
+
|
3
|
+
Write 6502 assembly with Ruby code.
|
4
|
+
|
5
|
+
This gem was specifically built to write Atari2600 games and comes packages with some the Ruby equivalent of the `vcs.h` and `macro.h` files. It is also in a very early stage of development and should be considered as a proof of concept. I also tried to make it less than 100 lines of code.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
```
|
10
|
+
gem install asm6502
|
11
|
+
```
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
Ruby is quite a powerful language when it comes to building DSL like configuration files... or 6502 assembly. Let's take an example from https://8bitworkshop.com:
|
16
|
+
|
17
|
+
```
|
18
|
+
; https://8bitworkshop.com/v3.7.0/?file=examples%2Fplayfield.a&platform=vcs
|
19
|
+
|
20
|
+
processor 6502
|
21
|
+
include "vcs.h"
|
22
|
+
include "macro.h"
|
23
|
+
|
24
|
+
org $f000
|
25
|
+
|
26
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
27
|
+
;
|
28
|
+
; We're going to mess with the playfield registers, PF0, PF1 and PF2.
|
29
|
+
; Between them, they represent 20 bits of bitmap information
|
30
|
+
; which are replicated over 40 wide pixels for each scanline.
|
31
|
+
; By changing the registers before each scanline, we can draw bitmaps.
|
32
|
+
;
|
33
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
34
|
+
|
35
|
+
Counter equ $81
|
36
|
+
|
37
|
+
Start CLEAN_START
|
38
|
+
|
39
|
+
NextFrame
|
40
|
+
; This macro efficiently gives us 1 + 3 lines of VSYNC
|
41
|
+
VERTICAL_SYNC
|
42
|
+
|
43
|
+
; 36 lines of VBLANK
|
44
|
+
ldx #36
|
45
|
+
LVBlank sta WSYNC
|
46
|
+
dex
|
47
|
+
bne LVBlank
|
48
|
+
; Disable VBLANK
|
49
|
+
stx VBLANK
|
50
|
+
; Set foreground color
|
51
|
+
lda #$82
|
52
|
+
sta COLUPF
|
53
|
+
; Draw the 192 scanlines
|
54
|
+
ldx #192
|
55
|
+
lda #0 ; changes every scanline
|
56
|
+
;lda Counter ; uncomment to scroll!
|
57
|
+
ScanLoop
|
58
|
+
sta WSYNC ; wait for next scanline
|
59
|
+
sta PF0 ; set the PF1 playfield pattern register
|
60
|
+
sta PF1 ; set the PF1 playfield pattern register
|
61
|
+
sta PF2 ; set the PF2 playfield pattern register
|
62
|
+
stx COLUBK ; set the background color
|
63
|
+
adc #1 ; increment A
|
64
|
+
dex
|
65
|
+
bne ScanLoop
|
66
|
+
|
67
|
+
; Reenable VBLANK for bottom (and top of next frame)
|
68
|
+
lda #2
|
69
|
+
sta VBLANK
|
70
|
+
; 30 lines of overscan
|
71
|
+
ldx #30
|
72
|
+
LVOver sta WSYNC
|
73
|
+
dex
|
74
|
+
bne LVOver
|
75
|
+
|
76
|
+
; Go back and do another frame
|
77
|
+
inc Counter
|
78
|
+
jmp NextFrame
|
79
|
+
|
80
|
+
org $fffc
|
81
|
+
.word Start
|
82
|
+
.word Start
|
83
|
+
```
|
84
|
+
|
85
|
+
The same code written using asm6502 will be something like:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require "asm6502"
|
89
|
+
include Asm6502
|
90
|
+
|
91
|
+
require 'asm6502/vcs'
|
92
|
+
|
93
|
+
# We're going to mess with the playfield registers, PF0, PF1 and PF2.
|
94
|
+
# Between them, they represent 20 bits of bitmap information
|
95
|
+
# which are replicated over 40 wide pixels for each scanline.
|
96
|
+
# By changing the registers before each scanline, we can draw bitmaps.
|
97
|
+
|
98
|
+
Org[0x0080]
|
99
|
+
Label[:counter, 1]
|
100
|
+
|
101
|
+
Output[ARGV[0]] do
|
102
|
+
Org[0xf000]
|
103
|
+
Label[:reset]
|
104
|
+
|
105
|
+
clean_start
|
106
|
+
|
107
|
+
Label[:next_frame]
|
108
|
+
# This macro efficiently gives us 1 + 3 lines of VSYNC
|
109
|
+
vertical_sync
|
110
|
+
|
111
|
+
# 36 lines of VBLANK
|
112
|
+
ldx 36
|
113
|
+
|
114
|
+
Label[:lv_blank]
|
115
|
+
|
116
|
+
sta :WSYNC
|
117
|
+
dex
|
118
|
+
bne :lv_blank
|
119
|
+
|
120
|
+
# Disable VBLANK
|
121
|
+
stx :VBLANK
|
122
|
+
|
123
|
+
# Set foreground color
|
124
|
+
lda 0x82
|
125
|
+
sta :COLUPF
|
126
|
+
|
127
|
+
# Draw the 192 scanline
|
128
|
+
ldx 192
|
129
|
+
lda 0 # changes every scanline
|
130
|
+
|
131
|
+
Label[:scan_loop]
|
132
|
+
|
133
|
+
sta :WSYNC # wait for next scanline
|
134
|
+
sta :PF0 # set the PF0 playfield pattern register
|
135
|
+
sta :PF1 # set the PF1 playfield pattern register
|
136
|
+
sta :PF2 # set the PF1 playfield pattern register
|
137
|
+
stx :COLUBK # set the background color
|
138
|
+
adc 1 # increment A
|
139
|
+
dex
|
140
|
+
bne :scan_loop
|
141
|
+
|
142
|
+
# Reenable VBLANK for bottom (and top of next fram
|
143
|
+
lda 2
|
144
|
+
sta :VBLANK
|
145
|
+
|
146
|
+
# 30 lines of overscan
|
147
|
+
ldx 30
|
148
|
+
|
149
|
+
Label[:lv_over]
|
150
|
+
sta :WSYNC
|
151
|
+
dex
|
152
|
+
bne :lv_over
|
153
|
+
|
154
|
+
# Go back and do another frame
|
155
|
+
inc :counter
|
156
|
+
jmp :next_frame
|
157
|
+
|
158
|
+
Org[0xfffa]
|
159
|
+
|
160
|
+
Mem[2] = :reset
|
161
|
+
Mem[2] = :reset
|
162
|
+
Mem[2] = :reset
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
As you can see, even though it looks like assembly, it it actually Ruby code: each operator is a Ruby fonction and there's also a few control stuff:
|
167
|
+
|
168
|
+
* `Org[addr]`: set the current memory index.
|
169
|
+
* `Label[name, size = 0]`: Give a name to the current memory index and increase it by `size`.
|
170
|
+
* `Output[path] { actual code to output }`: Everything in the block will be written in the final file `path`.
|
171
|
+
* `Mem[size] = value`: set the memory at the current memory index to value and increases it by `size`.
|
172
|
+
|
173
|
+
## Disclaimer
|
174
|
+
|
175
|
+
This is just a proof and concept, I'm not sure everything works properly, I'm not sure about the way it works and I'm pretty certain it doesn't handle every use case. I'll try later to build an actual game from scratch with it.
|
176
|
+
|
data/asm6502.gemspec
ADDED
data/lib/asm6502.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
module Asm6502
|
2
|
+
X, Y = [ 'x', 'y' ]
|
3
|
+
@@org = 0
|
4
|
+
@@labels = {}
|
5
|
+
@@mem = []
|
6
|
+
|
7
|
+
class Label
|
8
|
+
def self.[](value, size = 0)
|
9
|
+
@@labels[value] = @@org
|
10
|
+
@@org += size
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Org
|
15
|
+
def self.[](value) @@org = value; end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Output
|
19
|
+
def self.[](path, &block)
|
20
|
+
File.open(path, 'w+') do |f|
|
21
|
+
@@mem = []
|
22
|
+
block.call
|
23
|
+
f.write(@@mem.drop_while(&:nil?).reverse.drop_while(&:nil?).reverse.map(&:to_i).pack("c*"))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Mem < Struct.new(:length, :value)
|
29
|
+
def self.[]=(length, value)
|
30
|
+
value = value.kind_of?(Symbol) ? @@labels[value] : value
|
31
|
+
sprintf("%0#{value.size * 8 + 2}b", value)[2..-1].scan(/.{8}/).last(length).map { |i| i.to_i(2) }.reverse.each_with_index do |digit, index|
|
32
|
+
@@mem[@@org + index] = digit
|
33
|
+
end
|
34
|
+
@@org += length
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
{
|
39
|
+
adc: { im: 0x69, zp: 0x65, zx: 0x75, ab: 0x6d, ax: 0x7d, ay: 0x79, ix: 0x61, iy: 0x71 },
|
40
|
+
and: { im: 0x29, zp: 0x25, zx: 0x35, ab: 0x2d, ax: 0x3d, ay: 0x39, ix: 0x21, iy: 0x31 },
|
41
|
+
sbc: { im: 0xe9, zp: 0xe5, zx: 0xf5, ab: 0xed, ax: 0xfd, ay: 0xf9, ix: 0xe1, iy: 0xf1 },
|
42
|
+
asl: { ac: 0x0a, zp: 0x06, zx: 0x16, ab: 0x0e, ax: 0x1e }, bit: { zp: 0x24, ab: 0x2c },
|
43
|
+
cmp: { im: 0xc9, zp: 0xc5, zx: 0xd5, ab: 0xcd, ax: 0xdd, ay: 0xd9, ix: 0xc1, iy: 0xd1 },
|
44
|
+
cpx: { im: 0xe0, zp: 0xe4, ab: 0xec }, cpy: { im: 0xc0, zp: 0xc4, ab: 0xcc },
|
45
|
+
dec: { zp: 0xc6, zx: 0xd6, ab: 0xce, ax: 0xde },
|
46
|
+
ldx: { im: 0xa2, zp: 0xa6, zy: 0xb6, ab: 0xae, ay: 0xbe },
|
47
|
+
ldy: { im: 0xa0, zp: 0xa4, zx: 0xb4, ab: 0xac, ax: 0xbc },
|
48
|
+
eor: { im: 0x49, zp: 0x45, zx: 0x55, ab: 0x4d, ax: 0x5d, ay: 0x59, ix: 0x41, iy: 0x51 },
|
49
|
+
inc: { zp: 0xe6, zx: 0xf6, ab: 0xee, ax: 0xfe },
|
50
|
+
jmp: { ab: 0x4c, in: 0x6c }, sr: { ab: 0x20 }, lsr: { ac: 0x4a, zx: 0x46, ab: 0x4e, ax: 0x5e },
|
51
|
+
lda: { im: 0xa9, zp: 0xa5, zx: 0xb5, ab: 0xad, ax: 0xbd, ay: 0xb9, ix: 0xa1, iy: 0xb1 },
|
52
|
+
ora: { im: 0x09, zp: 0x05, zx: 0x15, ab: 0x0d, ax: 0x1d, ay: 0x19, ix: 0x01, iy: 0x11 },
|
53
|
+
rol: { ac: 0x2a, zp: 0x26, zx: 0x36, ab: 0x2e, ax: 0x3e },
|
54
|
+
ror: { ac: 0x6a, zp: 0x66, zx: 0x76, ab: 0x6e, ax: 0x7e },
|
55
|
+
sty: { zp: 0x84, zx: 0x94, ab: 0x8c }, stx: { zp: 0x86, zy: 0x96, ab: 0x8e },
|
56
|
+
sta: { zp: 0x85, zx: 0x95, ab: 0x8d, ax: 0x9d, ay: 0x99, ix: 0x81, iy: 0x91 },
|
57
|
+
bpl: { rl: 0x10 }, bmi: { rl: 0x30 }, bvc: { rl: 0x50 }, bne: { rl: 0xd0 },
|
58
|
+
bvs: { rl: 0x70 }, bcc: { rl: 0x90 }, bcs: { rl: 0xb0 }, beq: { rl: 0xf0 },
|
59
|
+
brk: 0x00, nop: 0xea, rti: 0x40, clc: 0x18, sec: 0x38, cli: 0x58, sei: 0x78, clv: 0xb8,
|
60
|
+
cld: 0xd8, sed: 0xf8, txs: 0x9a, tsx: 0xba, pha: 0x48, rts: 0x60, pla: 0x68, php: 0x08,
|
61
|
+
plp: 0x28, tax: 0xaa, txa: 0x8a, dex: 0xca, inx: 0xe8, tay: 0xa8, tya: 0x98, dey: 0x88, iny: 0xc8
|
62
|
+
}.each do |opcode, modes|
|
63
|
+
define_method(opcode) do |*args|
|
64
|
+
if args[0].nil? && modes.kind_of?(Integer)
|
65
|
+
[ modes ]
|
66
|
+
elsif modes[:ac] && args[0].nil?
|
67
|
+
[ modes[:ac] ]
|
68
|
+
elsif modes[:rl] && args[0].kind_of?(Symbol)
|
69
|
+
[ modes[:rl], @@labels[args[0]] - @@org - 2 ]
|
70
|
+
elsif modes[:im] && args[0].kind_of?(Integer) && args[0] < 0xff
|
71
|
+
[ modes[:im], args[0] ]
|
72
|
+
elsif modes[:zp] && args[0].kind_of?(Symbol) && @@labels[args[0]] < 0xff && args[1].nil?
|
73
|
+
[ modes[:zp], @@labels[args[0]] ]
|
74
|
+
elsif modes[:zx] && args[0].kind_of?(Symbol) && @@labels[args[0]] < 0xff && args[1].eql?(X)
|
75
|
+
[ modes[:zx], @@labels[args[0]] ]
|
76
|
+
elsif modes[:ab] && args[0].kind_of?(Symbol) && args[1].nil?
|
77
|
+
[ modes[:ab], @@labels[args[0]] & 0xff, @@labels[args[0]] >> 8 & 0xff ]
|
78
|
+
elsif modes[:ax] && args[0].kind_of?(Symbol) && args[1].eql?(X)
|
79
|
+
[ modes[:ax], @@labels[args[0]] & 0xff, @@labels[args[0]] >> 8 & 0xff ]
|
80
|
+
elsif modes[:ay] && args[0].kind_of?(Symbol) && args[1].eql?(Y)
|
81
|
+
[ modes[:ay], @@labels[args[0]] & 0xff, @@labels[args[0]] >> 8 & 0xff ]
|
82
|
+
elsif modes[:ix] && args[0].kind_of?(Array) && args[0].length == 2 && args[0][0].kind_of?(Symbol) && args[0][1].eql?(X) && args[1].nil?
|
83
|
+
[ modes[:ix], @@labels[args[0][0]] & 0xff ]
|
84
|
+
elsif modes[:iy] && args[0].kind_of?(Array) && args[0].length == 1 && args[0][0].kind_of?(Symbol) && args[1].eql?(Y)
|
85
|
+
[ modes[:iy], @@labels[args[0][0]] & 0xff ]
|
86
|
+
elsif modes[:in] && args[0].kind_of?(Array) && args[0].length == 1 && args[0][0].kind_of?(Symbol)
|
87
|
+
[ modes[:in], @@labels[args[0][0]] & 0xff, @@labels[args[0][0]] >> 8 & 0xff ]
|
88
|
+
else
|
89
|
+
raise ArgumentError.new("No suitable mode found for '#{opcode} #{args.join(" ")}'")
|
90
|
+
end.each do |opi|
|
91
|
+
Mem[1] = opi
|
92
|
+
end
|
93
|
+
rescue => error
|
94
|
+
raise ArgumentError.new("Unexpected error on '#{opcode} #{args.join(" ")}': #{error}")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/asm6502/vcs.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
# MACRO.H
|
2
|
+
# Version 1.06, 3/SEPTEMBER/2004
|
3
|
+
|
4
|
+
VERSION_MACRO = 106
|
5
|
+
|
6
|
+
#
|
7
|
+
# THIS FILE IS EXPLICITLY SUPPORTED AS A DASM-PREFERRED COMPANION FILE
|
8
|
+
# PLEASE DO *NOT* REDISTRIBUTE MODIFIED VERSIONS OF THIS FILE!
|
9
|
+
#
|
10
|
+
# This file defines DASM macros useful for development for the Atari 2600.
|
11
|
+
# It is distributed as a companion machine-specific support package
|
12
|
+
# for the DASM compiler. Updates to this file, DASM, and associated tools are
|
13
|
+
# available at at http://www.atari2600.org/dasm
|
14
|
+
#
|
15
|
+
# Many thanks to the people who have contributed. If you take issue with the
|
16
|
+
# contents, or would like to add something, please write to me
|
17
|
+
# (atari2600@taswegian.com) with your contribution.
|
18
|
+
#
|
19
|
+
# Latest Revisions...
|
20
|
+
#
|
21
|
+
# 1.06 03/SEP/2004 - nice revision of VERTICAL_BLANK (Edwin Blink)
|
22
|
+
# 1.05 14/NOV/2003 - Added VERSION_MACRO equate (which will reflect 100x version #)
|
23
|
+
# This will allow conditional code to verify MACRO.H being
|
24
|
+
# used for code assembly.
|
25
|
+
# 1.04 13/NOV/2003 - SET_POINTER macro added (16-bit address load)
|
26
|
+
#
|
27
|
+
# 1.03 23/JUN/2003 - CLEAN_START macro added - clears TIA, RAM, registers
|
28
|
+
#
|
29
|
+
# 1.02 14/JUN/2003 - VERTICAL_SYNC macro added
|
30
|
+
# (standardised macro for vertical synch code)
|
31
|
+
# 1.01 22/MAR/2003 - SLEEP macro added.
|
32
|
+
# - NO_ILLEGAL_OPCODES switch implemented
|
33
|
+
# 1.0 22/MAR/2003 Initial release
|
34
|
+
|
35
|
+
# Note: These macros use illegal opcodes. To disable illegal opcode usage,
|
36
|
+
# define the symbol NO_ILLEGAL_OPCODES (-DNO_ILLEGAL_OPCODES=1 on command-line).
|
37
|
+
# If you do not allow illegal opcode usage, you must include this file
|
38
|
+
# *after* including VCS.H (as the non-illegal opcodes access hardware
|
39
|
+
# registers and require them to be defined first).
|
40
|
+
|
41
|
+
# Available macros...
|
42
|
+
# SLEEP n - sleep for n cycles
|
43
|
+
# VERTICAL_SYNC - correct 3 scanline vertical synch code
|
44
|
+
# CLEAN_START - set machine to known state on startup
|
45
|
+
# SET_POINTER - load a 16-bit absolute to a 16-bit variable
|
46
|
+
|
47
|
+
#-------------------------------------------------------------------------------
|
48
|
+
# SLEEP duration
|
49
|
+
# Original author: Thomas Jentzsch
|
50
|
+
# Inserts code which takes the specified number of cycles to execute. This is
|
51
|
+
# useful for code where precise timing is required.
|
52
|
+
# ILLEGAL-OPCODE VERSION DOES NOT AFFECT FLAGS OR REGISTERS.
|
53
|
+
# LEGAL OPCODE VERSION MAY AFFECT FLAGS
|
54
|
+
# Uses illegal opcode (DASM 2.20.01 onwards).
|
55
|
+
|
56
|
+
def sleep(cycles) #usage: sleep n (n>1)
|
57
|
+
if cycles < 2
|
58
|
+
puts "MACRO ERROR: 'SLEEP': Duration must be > 1"
|
59
|
+
exit 1
|
60
|
+
end
|
61
|
+
|
62
|
+
if cycles & 1
|
63
|
+
bit :VSYNC
|
64
|
+
end
|
65
|
+
|
66
|
+
cycles -= 3
|
67
|
+
|
68
|
+
(cycles / 2).times do
|
69
|
+
nop
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#-------------------------------------------------------------------------------
|
74
|
+
# VERTICAL_SYNC
|
75
|
+
# revised version by Edwin Blink -- saves bytes!
|
76
|
+
# Inserts the code required for a proper 3 scanline vertical sync sequence
|
77
|
+
# Note: Alters the accumulator
|
78
|
+
|
79
|
+
# OUT: A = 0
|
80
|
+
|
81
|
+
def vertical_sync
|
82
|
+
lda 0b1110 #%1110 # each '1' bits generate a VSYNC ON line (bits 1..3)
|
83
|
+
Label[:VSLP1]
|
84
|
+
sta :WSYNC # 1st '0' bit resets Vsync, 2nd '0' bit exit loop
|
85
|
+
sta :VSYNC
|
86
|
+
lsr
|
87
|
+
bne :VSLP1 # branch until VYSNC has been reset
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
#-------------------------------------------------------------------------------
|
92
|
+
# CLEAN_START
|
93
|
+
# Original author: Andrew Davie
|
94
|
+
# Standardised start-up code, clears stack, all TIA registers and RAM to 0
|
95
|
+
# Sets stack pointer to $FF, and all registers to 0
|
96
|
+
# Sets decimal mode off, sets interrupt flag (kind of un-necessary)
|
97
|
+
# Use as very first section of code on boot (ie: at reset)
|
98
|
+
# Code written to minimise total ROM usage - uses weird 6502 knowledge :)
|
99
|
+
|
100
|
+
def clean_start
|
101
|
+
sei
|
102
|
+
cld
|
103
|
+
|
104
|
+
ldx 0
|
105
|
+
txa
|
106
|
+
tay
|
107
|
+
Label[:CLEAR_STACK]
|
108
|
+
dex
|
109
|
+
txs
|
110
|
+
pha
|
111
|
+
bne :CLEAR_STACK # SP=$FF, X = A = Y = 0
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
#-------------------------------------------------------
|
116
|
+
# SET_POINTER
|
117
|
+
# Original author: Manuel Rotschkar
|
118
|
+
#
|
119
|
+
# Sets a 2 byte RAM pointer to an absolute address.
|
120
|
+
#
|
121
|
+
# Usage: SET_POINTER pointer, address
|
122
|
+
# Example: SET_POINTER SpritePTR, SpriteData
|
123
|
+
#
|
124
|
+
# Note: Alters the accumulator, NZ flags
|
125
|
+
# IN 1: 2 byte RAM location reserved for pointer
|
126
|
+
# IN 2: absolute address
|
127
|
+
|
128
|
+
def set_pointer(pointer, address)
|
129
|
+
LDA #<.ADDRESS # Get Lowbyte of Address
|
130
|
+
STA .POINTER # Store in pointer
|
131
|
+
LDA #>.ADDRESS # Get Hibyte of Address
|
132
|
+
STA .POINTER+1 # Store in pointer+1
|
133
|
+
end
|
134
|
+
|
135
|
+
#-------------------------------------------------------
|
136
|
+
# BOUNDARY byte#
|
137
|
+
# Original author: Denis Debro (borrowed from Bob Smith / Thomas)
|
138
|
+
#
|
139
|
+
# Push data to a certain position inside a page and keep count of how
|
140
|
+
# many free bytes the programmer will have.
|
141
|
+
#
|
142
|
+
# eg: BOUNDARY 5 # position at byte #5 in page
|
143
|
+
|
144
|
+
# .FREE_BYTES SET 0
|
145
|
+
# MAC BOUNDARY
|
146
|
+
# REPEAT 256
|
147
|
+
# IF <. % {1} = 0
|
148
|
+
# MEXIT
|
149
|
+
# ELSE
|
150
|
+
# .FREE_BYTES SET .FREE_BYTES + 1
|
151
|
+
# .byte $00
|
152
|
+
# ENDIF
|
153
|
+
# REPEND
|
154
|
+
# ENDM
|
155
|
+
|
156
|
+
|
157
|
+
# EOF
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require "asm6502"
|
2
|
+
include Asm6502
|
3
|
+
|
4
|
+
TIA_BASE_ADDRESS = 0
|
5
|
+
TIA_BASE_READ_ADDRESS = TIA_BASE_ADDRESS
|
6
|
+
TIA_BASE_WRITE_ADDRESS = TIA_BASE_ADDRESS
|
7
|
+
|
8
|
+
Org[TIA_BASE_WRITE_ADDRESS]
|
9
|
+
|
10
|
+
Label[:VSYNC, 1] # $00 0000 00x0 Vertical Sync Set-Clear
|
11
|
+
Label[:VBLANK, 1] # $01 xx00 00x0 Vertical Blank Set-Clear
|
12
|
+
Label[:WSYNC, 1] # $02 ---- ---- Wait for Horizontal Blank
|
13
|
+
Label[:RSYNC, 1] # $03 ---- ---- Reset Horizontal Sync Counter
|
14
|
+
Label[:NUSIZ0, 1] # $04 00xx 0xxx Number-Size player/missle 0
|
15
|
+
Label[:NUSIZ1, 1] # $05 00xx 0xxx Number-Size player/missle 1
|
16
|
+
Label[:COLUP0, 1] # $06 xxxx xxx0 Color-Luminance Player 0
|
17
|
+
Label[:COLUP1, 1] # $07 xxxx xxx0 Color-Luminance Player 1
|
18
|
+
Label[:COLUPF, 1] # $08 xxxx xxx0 Color-Luminance Playfield
|
19
|
+
Label[:COLUBK, 1] # $09 xxxx xxx0 Color-Luminance Background
|
20
|
+
Label[:CTRLPF, 1] # $0A 00xx 0xxx Control Playfield, Ball, Collisions
|
21
|
+
Label[:REFP0, 1] # $0B 0000 x000 Reflection Player 0
|
22
|
+
Label[:REFP1, 1] # $0C 0000 x000 Reflection Player 1
|
23
|
+
Label[:PF0, 1] # $0D xxxx 0000 Playfield Register Byte 0
|
24
|
+
Label[:PF1, 1] # $0E xxxx xxxx Playfield Register Byte 1
|
25
|
+
Label[:PF2, 1] # $0F xxxx xxxx Playfield Register Byte 2
|
26
|
+
Label[:RESP0, 1] # $10 ---- ---- Reset Player 0
|
27
|
+
Label[:RESP1, 1] # $11 ---- ---- Reset Player 1
|
28
|
+
Label[:RESM0, 1] # $12 ---- ---- Reset Missle 0
|
29
|
+
Label[:RESM1, 1] # $13 ---- ---- Reset Missle 1
|
30
|
+
Label[:RESBL, 1] # $14 ---- ---- Reset Ball
|
31
|
+
Label[:AUDC0, 1] # $15 0000 xxxx Audio Control 0
|
32
|
+
Label[:AUDC1, 1] # $16 0000 xxxx Audio Control 1
|
33
|
+
Label[:AUDF0, 1] # $17 000x xxxx Audio Frequency 0
|
34
|
+
Label[:AUDF1, 1] # $18 000x xxxx Audio Frequency 1
|
35
|
+
Label[:AUDV0, 1] # $19 0000 xxxx Audio Volume 0
|
36
|
+
Label[:AUDV1, 1] # $1A 0000 xxxx Audio Volume 1
|
37
|
+
Label[:GRP0, 1] # $1B xxxx xxxx Graphics Register Player 0
|
38
|
+
Label[:GRP1, 1] # $1C xxxx xxxx Graphics Register Player 1
|
39
|
+
Label[:ENAM0, 1] # $1D 0000 00x0 Graphics Enable Missle 0
|
40
|
+
Label[:ENAM1, 1] # $1E 0000 00x0 Graphics Enable Missle 1
|
41
|
+
Label[:ENABL, 1] # $1F 0000 00x0 Graphics Enable Ball
|
42
|
+
Label[:HMP0, 1] # $20 xxxx 0000 Horizontal Motion Player 0
|
43
|
+
Label[:HMP1, 1] # $21 xxxx 0000 Horizontal Motion Player 1
|
44
|
+
Label[:HMM0, 1] # $22 xxxx 0000 Horizontal Motion Missle 0
|
45
|
+
Label[:HMM1, 1] # $23 xxxx 0000 Horizontal Motion Missle 1
|
46
|
+
Label[:HMBL, 1] # $24 xxxx 0000 Horizontal Motion Ball
|
47
|
+
Label[:VDELP0, 1] # $25 0000 000x Vertical Delay Player 0
|
48
|
+
Label[:VDELP1, 1] # $26 0000 000x Vertical Delay Player 1
|
49
|
+
Label[:VDELBL, 1] # $27 0000 000x Vertical Delay Ball
|
50
|
+
Label[:RESMP0, 1] # $28 0000 00x0 Reset Missle 0 to Player 0
|
51
|
+
Label[:RESMP1, 1] # $29 0000 00x0 Reset Missle 1 to Player 1
|
52
|
+
Label[:HMOVE, 1] # $2A ---- ---- Apply Horizontal Motion
|
53
|
+
Label[:HMCLR, 1] # $2B ---- ---- Clear Horizontal Move Registers
|
54
|
+
Label[:CXCLR, 1] # $2C ---- ---- Clear Collision Latches
|
55
|
+
|
56
|
+
Org[TIA_BASE_READ_ADDRESS]
|
57
|
+
|
58
|
+
Label[:CXM0P, 1] # $00 xx00 0000 Read Collision M0-P1 M0-P0
|
59
|
+
Label[:CXM1P, 1] # $01 xx00 0000 M1-P0 M1-P1
|
60
|
+
Label[:CXP0FB, 1] # $02 xx00 0000 P0-PF P0-BL
|
61
|
+
Label[:CXP1FB, 1] # $03 xx00 0000 P1-PF P1-BL
|
62
|
+
Label[:CXM0FB, 1] # $04 xx00 0000 M0-PF M0-BL
|
63
|
+
Label[:CXM1FB, 1] # $05 xx00 0000 M1-PF M1-BL
|
64
|
+
Label[:CXBLPF, 1] # $06 x000 0000 BL-PF -----
|
65
|
+
Label[:CXPPMM, 1] # $07 xx00 0000 P0-P1 M0-M1
|
66
|
+
Label[:INPT0, 1] # $08 x000 0000 Read Pot Port 0
|
67
|
+
Label[:INPT1, 1] # $09 x000 0000 Read Pot Port 1
|
68
|
+
Label[:INPT2, 1] # $0A x000 0000 Read Pot Port 2
|
69
|
+
Label[:INPT3, 1] # $0B x000 0000 Read Pot Port 3
|
70
|
+
Label[:INPT4, 1] # $0C x000 0000 Read Input (Trigger) 0
|
71
|
+
Label[:INPT5, 1] # $0D x000 0000 Read Input (Trigger) 1
|
72
|
+
|
73
|
+
Org[0x280]
|
74
|
+
|
75
|
+
Label[:SWCHA, 1] # $280 Port A data register for joysticks:
|
76
|
+
# Bits 4-7 for player 1. Bits 0-3 for player 2.
|
77
|
+
|
78
|
+
Label[:SWACNT, 1] # $281 Port A data direction register (DDR)
|
79
|
+
Label[:SWCHB, 1] # $282 Port B data (console switches)
|
80
|
+
Label[:SWBCNT, 1] # $283 Port B DDR
|
81
|
+
Label[:INTIM, 1] # $284 Timer output
|
82
|
+
|
83
|
+
Label[:TIMINT, 1] # $285
|
84
|
+
|
85
|
+
# Unused/undefined registers ($285-$294)
|
86
|
+
Label[nil, 14]
|
87
|
+
|
88
|
+
Label[:TIM1T, 1] # $294 set 1 clock interval
|
89
|
+
Label[:TIM8T, 1] # $295 set 8 clock interval
|
90
|
+
Label[:TIM64T, 1] # $296 set 64 clock interval
|
91
|
+
Label[:T1024T, 1] # $297 set 1024 clock interval
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: asm6502
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gallimimus
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-12-31 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- ".gitignore"
|
20
|
+
- README.md
|
21
|
+
- asm6502.gemspec
|
22
|
+
- lib/asm6502.rb
|
23
|
+
- lib/asm6502/vcs.rb
|
24
|
+
- lib/asm6502/vcs/macro.rb
|
25
|
+
- lib/asm6502/vcs/vcs.rb
|
26
|
+
homepage:
|
27
|
+
licenses: []
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubygems_version: 3.0.3
|
45
|
+
signing_key:
|
46
|
+
specification_version: 4
|
47
|
+
summary: Write 6502 assembly from Ruby code
|
48
|
+
test_files: []
|