git @ Cat's Eye Technologies SixtyPical / 0093c7b
First cut at support for targetting the Atari 2600. Chris Pressey 3 years ago
7 changed file(s) with 551 addition(s) and 6 deletion(s). Raw diff Collapse all Expand all
77 be used in most places where literal values can be used.
88 * Specifying multiple SixtyPical source files will produce a single
99 compiled result from their combination.
10 * Rudimentary support for Atari 2600 prelude in a 4K cartridge image,
11 and start of an example program in `eg/atari2600` directory.
1012
1113 0.14
1214 ----
8080 start_addr = 0x1001
8181 prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34,
8282 0x31, 0x30, 0x39, 0x00, 0x00, 0x00]
83 elif options.prelude == 'atari2600':
84 output_format = 'crtbb'
85 start_addr = 0xf000
86 prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9,
87 0x00,0x95, 0x00, 0xca, 0xd0, 0xfb]
88
8389 elif options.prelude:
8490 raise NotImplementedError("Unknown prelude: {}".format(options.prelude))
8591
9399 emitter.emit(Byte(byte))
94100 compiler = Compiler(emitter)
95101 compiler.compile_program(program)
102
103 # If we are outputting a cartridge with boot and BRK address
104 # at the end, pad to ROM size minus 4 bytes, and emit addresses.
105 if output_format == 'crtbb':
106 emitter.pad_to_size(4096 - 4)
107 emitter.emit(Word(start_addr))
108 emitter.emit(Word(start_addr))
109
96110 if options.debug:
97111 pprint(emitter.accum)
98112 else:
118132 )
119133 argparser.add_argument(
120134 "--output-format", type=str, default='prg',
121 help="Executable format to produce. Options are: prg (.PRG file "
122 "for Commodore 8-bit). Default: prg."
135 help="Executable format to produce. Options are: prg, crtbb. "
136 "Default: prg."
123137 )
124138 argparser.add_argument(
125139 "--prelude", type=str,
126 help="Insert a snippet before the compiled program "
127 "so that it can be LOADed and RUN on a certain platforms. "
140 help="Insert a snippet of code before the compiled program so that "
141 "it can be booted automatically on a particular platform. "
128142 "Also sets the origin and format. "
129 "Options are: c64 or vic20."
143 "Options are: c64, vic20, atari2600."
130144 )
131145 argparser.add_argument(
132146 "--debug",
3737 Commodore VIC-20. The directory itself contains some simple demos,
3838 for example [hearts.60p](vic20/hearts.60p).
3939
40 ### atari2600
41
42 In the [vic20](vic20/) directory are programs that run on the
43 Atari 2600 (4K cartridge). The directory itself contains a simple
44 demo, [atari-2600-example.60p](atari2600/atari-2600-example.60p).
45 (Doesn't work yet.)
46
4047 [Ophis]: http://michaelcmartin.github.io/Ophis/
0 // atari-2600-example.60p - SixtyPical translation of atari-2600-example.oph
1
2 byte VSYNC @ $00
3 byte VBLANK @ $01
4 byte WSYNC @ $02
5 byte NUSIZ0 @ $04
6 byte NUSIZ1 @ $05
7 byte COLUPF @ $08
8 byte COLUBK @ $09
9 byte PF0 @ $0D
10 byte PF1 @ $0E
11 byte PF2 @ $0F
12 byte SWCHA @ $280
13 byte INTIM @ $284
14 byte TIM64T @ $296
15 byte CTRLPF @ $0A
16 byte COLUP0 @ $06
17 byte COLUP1 @ $07
18 byte GP0 @ $1B
19 byte GP1 @ $1C
20 byte HMOVE @ $2a
21 byte RESP0 @ $10
22 byte RESP1 @ $11
23
24 byte colour @ $80
25 byte luminosity @ $81
26 byte joystick_delay @ $82
27
28 byte table[8] image_data : "ZZZZUUUU"
29 // %01111110
30 // %10000001
31 // %10011001
32 // %10100101
33 // %10000001
34 // %10100101
35 // %10000001
36 // %01111110
37
38
39 define vertical_blank routine
40 outputs VSYNC, WSYNC, TIM64T
41 trashes a, x, z, n
42 {
43 ld x, $00
44 ld a, $02
45 st a, WSYNC
46 st a, WSYNC
47 st a, WSYNC
48 st a, VSYNC
49 st a, WSYNC
50 st a, WSYNC
51 ld a, $2C
52 st a, TIM64T
53 ld a, $00
54 st a, WSYNC
55 st a, VSYNC
56 }
57
58 define display_frame routine
59 inputs INTIM, image_data
60 outputs WSYNC, HMOVE, VBLANK, RESP0, GP0, PF0, PF1, PF2, COLUPF, COLUBK
61 trashes a, x, y, z, n
62 {
63 repeat {
64 ld a, INTIM
65 } until z
66
67 //; (After that loop finishes, we know the accumulator must contain 0.)
68
69 st a, WSYNC
70 st a, HMOVE
71 st a, VBLANK
72
73 //;
74 //; Wait for $3f (plus one?) scan lines to pass, by waiting for
75 //; WSYNC that many times.
76 //;
77
78 ld x, $3F
79 repeat {
80 st a, WSYNC
81 dec x
82 } until z // FIXME orig loop used "bpl _wsync_loop"
83 st a, WSYNC
84
85 //;
86 //; Delay while the raster scans across the screen. The more
87 //; we delay here, the more to the right the player will be when
88 //; we draw it.
89 //;
90
91 //// nop
92 //// nop
93 //// nop
94 //// nop
95 //// nop
96 //// nop
97 //// nop
98 //// nop
99 //// nop
100 //// nop
101 //// nop
102 //// nop
103 //// nop
104 //// nop
105 //// nop
106
107 //;
108 //; OK, *now* display the player.
109 //;
110
111 st a, RESP0
112
113 //;
114 //; Loop over the rows of the sprite data, drawing each to the screen
115 //; over four scan lines.
116 //;
117 //; TODO understand this better and describe it!
118 //;
119
120 ld y, $07
121 for y down to 0 {
122 ld a, image_data + y
123 st a, GP0
124
125 st a, WSYNC
126 st a, WSYNC
127 st a, WSYNC
128 st a, WSYNC
129 } // FIXME original was "dec y; bpl _image_loop"
130
131 ld a, $00
132 st a, GP0
133
134 //;
135 //; Turn off screen display and clear display registers.
136 //;
137
138 ld a, $02
139 st a, WSYNC
140 st a, VBLANK
141 ld a, $00
142 st a, PF0
143 st a, PF1
144 st a, PF2
145 st a, COLUPF
146 st a, COLUBK
147 }
148
149 define main routine
150 inputs image_data, INTIM
151 outputs CTRLPF, colour, luminosity, NUSIZ0, VSYNC, WSYNC, TIM64T, HMOVE, VBLANK, RESP0, GP0, PF0, PF1, PF2, COLUPF, COLUBK
152 trashes a, x, y, z, n
153 {
154 ld a, $00
155 st a, CTRLPF
156 ld a, $0c
157 st a, colour
158 ld a, $0a
159 st a, luminosity
160 ld a, $00
161 st a, NUSIZ0
162 repeat {
163 call vertical_blank
164 call display_frame
165 // call read_joystick
166 } forever
167 }
0 ;
1 ; atari-2600-example.oph
2 ; Skeleton code for an Atari 2600 ROM,
3 ; plus an example of reading the joystick.
4 ; By Chris Pressey, November 2, 2012.
5 ;
6 ; This work is in the public domain. See the file UNLICENSE for more info.
7 ;
8 ; Based on Chris Cracknell's Atari 2600 clock (also in the public domain):
9 ; http://everything2.com/title/An+example+of+Atari+2600+source+code
10 ;
11 ; to build and run in Stella:
12 ; ophis atari-2600-example.oph -o example.bin
13 ; stella example.bin
14 ;
15 ; More useful information can be found in the Stella Programmer's Guide:
16 ; http://alienbill.com/2600/101/docs/stella.html
17 ;
18
19 ;
20 ; Useful system addresses (TODO: briefly describe each of these.)
21 ;
22
23 .alias VSYNC $00
24 .alias VBLANK $01
25 .alias WSYNC $02
26 .alias NUSIZ0 $04
27 .alias NUSIZ1 $05
28 .alias COLUPF $08
29 .alias COLUBK $09
30 .alias PF0 $0D
31 .alias PF1 $0E
32 .alias PF2 $0F
33 .alias SWCHA $280
34 .alias INTIM $284
35 .alias TIM64T $296
36 .alias CTRLPF $0A
37 .alias COLUP0 $06
38 .alias COLUP1 $07
39 .alias GP0 $1B
40 .alias GP1 $1C
41 .alias HMOVE $2a
42 .alias RESP0 $10
43 .alias RESP1 $11
44
45 ;
46 ; Cartridge ROM occupies the top 4K of memory ($F000-$FFFF).
47 ; Thus, typically, the program will occupy all that space too.
48 ;
49 ; Zero-page RAM we can use with impunity starts at $80 and goes
50 ; upward (at least until $99, but probably further.)
51 ;
52
53 .alias colour $80
54 .alias luminosity $81
55 .alias joystick_delay $82
56
57 .org $F000
58
59 ;
60 ; Standard prelude for Atari 2600 cartridge code.
61 ;
62 ; Get various parts of the machine into a known state:
63 ;
64 ; - Disable interrupts
65 ; - Clear the Decimal flag
66 ; - Initialize the Stack Pointer
67 ; - Zero all bytes in Zero Page memory
68 ;
69
70 start:
71 sei
72 cld
73 ldx #$FF
74 txs
75 lda #$00
76
77 zero_loop:
78 sta $00, x
79 dex
80 bne zero_loop
81
82 ; and fall through to...
83
84 ;
85 ; Initialization.
86 ;
87 ; - Clear the Playfield Control register.
88 ; - Set the player (sprite) colour to light green (write to COLUP0.)
89 ; - Set the player (sprite) size/repetion to normal (write to NUSIZ0.)
90 ;
91
92 lda #$00
93 sta CTRLPF
94 lda #$0c
95 sta colour
96 lda #$0a
97 sta luminosity
98 lda #$00
99 sta NUSIZ0
100
101 ; and fall through to...
102
103 ;
104 ; Main loop.
105 ;
106 ; A typical main loop consists of:
107 ; - Waiting for the frame to start (vertical blank period)
108 ; - Displaying stuff on the screen (the _display kernel_)
109 ; - Doing any processing you like (reading joysticks, updating program state,
110 ; etc.), as long as you get it all done before the next frame starts!
111 ;
112
113 main:
114 jsr vertical_blank
115 jsr display_frame
116 jsr read_joystick
117 jmp main
118
119 ;
120 ; Vertical blank routine.
121 ;
122 ; In brief: wait until it is time for the next frame of video.
123 ; TODO: describe this in more detail.
124 ;
125
126 vertical_blank:
127 ldx #$00
128 lda #$02
129 sta WSYNC
130 sta WSYNC
131 sta WSYNC
132 sta VSYNC
133 sta WSYNC
134 sta WSYNC
135 lda #$2C
136 sta TIM64T
137 lda #$00
138 sta WSYNC
139 sta VSYNC
140 rts
141
142 ;
143 ; Display kernal.
144 ;
145 ; First, wait until it's time to display the frame.
146 ;
147
148 .scope
149 display_frame:
150 lda INTIM
151 bne display_frame
152
153 ;
154 ; (After that loop finishes, we know the accumulator must contain 0.)
155 ; Wait for the next scanline, zero HMOVE (for some reason; TODO discover
156 ; this), then turn on the screen.
157 ;
158
159 sta WSYNC
160 sta HMOVE
161 sta VBLANK
162
163 ;
164 ; Actual work in the display kernal is done here.
165 ;
166 ; This is a pathological approach to writing a display kernal.
167 ; This wouldn't be how you'd do things in a game. So be it.
168 ; One day I may improve it. For now, be happy that it displays
169 ; anything at all!
170 ;
171
172 ;
173 ; Wait for $3f (plus one?) scan lines to pass, by waiting for
174 ; WSYNC that many times.
175 ;
176
177 ldx #$3F
178 _wsync_loop:
179 sta WSYNC
180 dex
181 bpl _wsync_loop
182 sta WSYNC
183
184 ;
185 ; Delay while the raster scans across the screen. The more
186 ; we delay here, the more to the right the player will be when
187 ; we draw it.
188 ;
189
190 nop
191 nop
192 nop
193 nop
194 nop
195 nop
196 nop
197 nop
198 nop
199 nop
200 nop
201 nop
202 nop
203 nop
204 nop
205
206 ;
207 ; OK, *now* display the player.
208 ;
209
210 sta RESP0
211
212 ;
213 ; Loop over the rows of the sprite data, drawing each to the screen
214 ; over four scan lines.
215 ;
216 ; TODO understand this better and describe it!
217 ;
218
219 ldy #$07
220 _image_loop:
221 lda image_data, y
222 sta GP0
223
224 sta WSYNC
225 sta WSYNC
226 sta WSYNC
227 sta WSYNC
228 dey
229 bpl _image_loop
230
231 lda #$00
232 sta GP0
233
234 ;
235 ; Turn off screen display and clear display registers.
236 ;
237
238 lda #$02
239 sta WSYNC
240 sta VBLANK
241 lda #$00
242 sta PF0
243 sta PF1
244 sta PF2
245 sta COLUPF
246 sta COLUBK
247
248 rts
249 .scend
250
251
252 ;
253 ; Read the joystick and use it to modify the colour and luminosity
254 ; of the player.
255 ;
256
257 .scope
258 read_joystick:
259 lda joystick_delay
260 beq _continue
261
262 dec joystick_delay
263 rts
264
265 _continue:
266 lda SWCHA
267 and #$f0
268 cmp #$e0
269 beq _up
270 cmp #$d0
271 beq _down
272 cmp #$b0
273 beq _left
274 cmp #$70
275 beq _right
276 jmp _tail
277
278 _up:
279 inc luminosity
280 jmp _tail
281 _down:
282 dec luminosity
283 jmp _tail
284 _left:
285 dec colour
286 jmp _tail
287 _right:
288 inc colour
289 ;jmp _tail
290
291 _tail:
292 lda colour
293 and #$0f
294 sta colour
295
296 lda luminosity
297 and #$0f
298 sta luminosity
299
300 lda colour
301 clc
302 rol
303 rol
304 rol
305 rol
306 ora luminosity
307 sta COLUP0
308
309 lda #$06
310 sta joystick_delay
311
312 rts
313 .scend
314
315 ;
316 ; Player (sprite) data.
317 ;
318 ; Because we loop over these bytes with the Y register counting *down*,
319 ; this image is stored "upside-down".
320 ;
321
322 image_data:
323 .byte %01111110
324 .byte %10000001
325 .byte %10011001
326 .byte %10100101
327 .byte %10000001
328 .byte %10100101
329 .byte %10000001
330 .byte %01111110
331
332 ;
333 ; Standard postlude for Atari 2600 cartridge code.
334 ; Give BRK and boot vectors that point to the start of the code.
335 ;
336
337 .advance $FFFC
338 .word start
339 .word start
00 #!/bin/sh
11
2 usage="Usage: loadngo.sh (c64|vic20) [--dry-run] <source.60p>"
2 usage="Usage: loadngo.sh (c64|vic20|atari2600) [--dry-run] <source.60p>"
33
44 arch="$1"
55 shift 1
1717 else
1818 emu="xvic"
1919 fi
20 elif [ "X$arch" = "Xatari2600" ]; then
21 prelude='atari2600'
22 emu='stella'
2023 else
2124 echo $usage && exit 1
2225 fi
185185 advance the address for the next label, but don't emit anything."""
186186 self.resolve_label(label)
187187 self.addr += label.length
188
189 def size(self):
190 return sum(emittable.size() for emittable in self.accum)
191
192 def pad_to_size(self, size):
193 self_size = self.size()
194 if self_size > size:
195 raise IndexError("Emitter size {} exceeds pad size {}".format(self_size, size))
196 num_bytes = size - self_size
197 if num_bytes > 0:
198 self.accum.extend([Byte(0)] * num_bytes)