1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
|
// Rewritten from https://github.com/BigEvilCorporation/megadrive_samples/blob/313e16db9c8cdd0bcd0c98223b3d4245f921b31d/3_sprites/sprites.asm
//==============================================================
// SEGA MEGA DRIVE/GENESIS - DEMO 3 - SPRITES SAMPLE
//==============================================================
// by Big Evil Corporation
//==============================================================
// A small, discreet, and complete sprites sample, with a healthy
// dose of comments and explanations for beginners.
// Runs on genuine hardware, and (hopefully) all emulators.
//
// I recommend reading and understanding the Scroll Planes
// sample first.
//
// To assemble this program with ASM68K.EXE:
// ASM68K.EXE /p sprites.asm,sprites.bin,sprites.map,sprites.lst
//
// To assemble this program with SNASM68K.EXE:
// SNASM68K.EXE /p sprites.asm,sprites.map,sprites.lst,sprites.bin
//
// sprites.asm = this source file
// sprites.bin = the binary file, fire this up in your emulator!
// sprites.lst = listing file, shows assembled addresses alongside
// your source code, open in a text editor
// sprites.map = symbol map file for linking (unused)
//==============================================================
#include <stdint.h>
#include <stddef.h>
//==============================================================
// CONSTANTS
//==============================================================
// VDP port addresses
#define VDP_DATA ((volatile uint16_t*)0x00C00000L)
#define VDP_CONTROL ((volatile uint16_t*)0x00C00004L)
// VDP commands
#define VDP_CMD_VRAM_WRITE ((uint32_t)0x40000000L)
#define VDP_CMD_CRAM_WRITE ((uint32_t)0xC0000000L)
#define VDP_CMD_VSRAM_WRITE ((uint32_t)0x40000010L)
// VDP memory addresses
// according to VDP registers 0x2, 0x4, and 0xD (see table above)
#define VRAM_ADDR_TILES (0x0000)
#define VRAM_ADDR_PLANE_A (0xC000)
#define VRAM_ADDR_PLANE_B (0xE000)
#define VRAM_ADDR_SPRITE_TABLE (0xF000) // NEW in this demo - the Sprite Attribute Table (SAT)
#define VRAM_ADDR_HSCROLL (0xFC00)
// Screen width and height (in pixels)
#define VDP_SCREEN_WIDTH (0x0140)
#define VDP_SCREEN_HEIGHT (0x00F0)
// The plane width and height (in tiles)
// according to VDP register 0x10 (see table above)
#define VDP_PLANE_WIDTH (0x40)
#define VDP_PLANE_HEIGHT (0x20)
// The size of the sprite plane (512x512 pixels)
//
// With only a 320x240 display size, a lot of this
// is off screen, which is useful for hiding sprites
// when not needed (saves needing to adjust the linked
// list in the attribute table).
#define VDP_SPRITE_PLANE_WIDTH (0x0200)
#define VDP_SPRITE_PLANE_HEIGHT (0x0200)
// The sprite border (invisible area left + top) size
//
// The sprite plane is 512x512 pixels, but is offset by
// -128 pixels in both X and Y directions. To see a sprite
// on screen at 0,0 we need to offset its position by
// this border.
#define VDP_SPRITE_BORDER_X (0x80)
#define VDP_SPRITE_BORDER_Y (0x80)
// Hardware version address
#define HARDWARE_VER_ADDRESS ((uint8_t*)0x00A10001L)
// TMSS
#define TMSS_ADDRESS ((uint32_t*)0x00A14000L)
#define TMSS_SIGNATURE (('S' << 24) | ('E' << 16) | ('G' << 8) | 'A')
// The size of a word and longword
#define SIZE_WORD (2)
#define SIZE_LONG (4)
// The size of one palette (in bytes, words, and longwords)
#define SIZE_PALETTE_B (0x20)
#define SIZE_PALETTE_W (SIZE_PALETTE_B/SIZE_WORD)
#define SIZE_PALETTE_L (SIZE_PALETTE_B/SIZE_LONG)
// The size of one graphics tile (in bytes, words, and longwords)
#define SIZE_TILE_B (0x20)
#define SIZE_TILE_W (SIZE_TILE_B/SIZE_WORD)
#define SIZE_TILE_L (SIZE_TILE_B/SIZE_LONG)
// Text draw position (in tiles)
#define TEXT_POS_X (0x02)
#define TEXT_POS_Y (0x04)
// Sprite initial draw positions (in pixels)
#define SPRITE_1_START_POS_X (VDP_SPRITE_BORDER_X)
#define SPRITE_1_START_POS_Y (VDP_SPRITE_BORDER_Y)
#define SPRITE_2_START_POS_X (VDP_SPRITE_BORDER_X + 0x0040)
#define SPRITE_2_START_POS_Y (VDP_SPRITE_BORDER_Y + 0x0020)
// Speed (in pixels per frame) to move our sprites
#define SPRITE_1_MOVE_SPEED_X (0x1)
#define SPRITE_1_MOVE_SPEED_Y (0x1)
#define SPRITE_2_MOVE_SPEED_X (0x2)
#define SPRITE_2_MOVE_SPEED_Y (0x0)
//==============================================================
// MEMORY MAP
//==============================================================
// We need to store the current sprite positions in RAM and update
// them each frame. There are a few ways to create a memory map,
// but the cleanest, simplest, and easiest to maintain method
// uses the assembler's "RS" keywords. RSSET begins a new table of
// offsets starting from any other offset (here we're starting at
// 0x00FF0000, the start of RAM), and allows us to add named entries
// of any size for the "variables". We can then read/write these
// variables using the offsets' labels (see INT_VInterrupt for use
// cases).
//==============================================================
#define RAM_SPRITE_1_POS_X ((uint16_t*)0x00FF0000) // 1 table entry of word size for sprite 1's X pos
#define RAM_SPRITE_1_POS_Y ((uint16_t*)0x00FF0002) // 1 table entry of word size for sprite 1's Y pos
#define RAM_SPRITE_2_POS_X ((uint16_t*)0x00FF0004) // 1 table entry of word size for sprite 2's X pos
#define RAM_SPRITE_2_POS_Y ((uint16_t*)0x00FF0006) // 1 table entry of word size for sprite 2's Y pos
// !! Be careful when adding any table entries of BYTE size, since
// you'll need to start worrying about alignment. More of this in a
// future demo.
// Number of palettes to write to CRAM
#define PALETTE_COUNT (0x2)
//==============================================================
// TILE IDs
//==============================================================
// The indices of the first tile in each sprite. We only need
// to tell the sprite table where to find the starting tile of
// each sprite, so we don't bother keeping track of every tile
// index.
//
// Note we still leave the first tile blank (planes A and B are
// filled with tile 0) so we'll be uploading our sprite tiles
// from index 1.
//
// See bottom of the file for the sprite tiles themselves.
//==============================================================
#define TILE_ID_BLANK (0x00) // The blank tile at index 0
#define TILE_ID_SPRITE_1 (0x01) // Sprite 1 index (4 tiles)
#define TILE_ID_SPRITE_2 (0x05) // Sprite 2 index (12 tiles)
// Total number of tiles in the sprites to upload to VRAM
#define TILE_COUNT (0x11) // Total tiles = 16
//==============================================================
// INITIAL VDP REGISTER VALUES
//==============================================================
// In this demo, we're particularly interested in register 0x5,
// which specifies the address of the Sprite Attribute Table
// (SAT) within VRAM. Here it's set to 0xF000.
//==============================================================
static const uint8_t vdp_registers[] = {
0x14, // 0x00: H interrupt on, palettes on
0x74, // 0x01: V interrupt on, display on, DMA on, Genesis mode on
0x30, // 0x02: Pattern table for Scroll Plane A at VRAM 0xC000 (bits 3-5 = bits 13-15)
0x00, // 0x03: Pattern table for Window Plane at VRAM 0x0000 (disabled) (bits 1-5 = bits 11-15)
0x07, // 0x04: Pattern table for Scroll Plane B at VRAM 0xE000 (bits 0-2 = bits 11-15)
0x78, // 0x05: Sprite Attribute Table at VRAM 0xF000 (bits 0-6 = bits 9-15)
0x00, // 0x06: Unused
0x00, // 0x07: Background colour: bits 0-3 = colour, bits 4-5 = palette
0x00, // 0x08: Unused
0x00, // 0x09: Unused
0x08, // 0x0A: Frequency of Horiz. interrupt in Rasters (number of lines travelled by the beam)
0x00, // 0x0B: External interrupts off, V scroll per-page, H scroll per-page
0x81, // 0x0C: Shadows and highlights off, interlace off, H40 mode (320 x 224 screen res)
0x3F, // 0x0D: Horiz. scroll table at VRAM 0xFC00 (bits 0-5)
0x00, // 0x0E: Unused
0x02, // 0x0F: Autoincrement 2 bytes
0x01, // 0x10: Scroll plane size: 64x32 tiles
0x00, // 0x11: Window Plane X pos 0 left (pos in bits 0-4, left/right in bit 7)
0x00, // 0x12: Window Plane Y pos 0 up (pos in bits 0-4, up/down in bit 7)
0xFF, // 0x13: DMA length lo byte
0xFF, // 0x14: DMA length hi byte
0x00, // 0x15: DMA source address lo byte
0x00, // 0x16: DMA source address mid byte
0x80, // 0x17: DMA source address hi byte, memory-to-VRAM mode (bits 6-7)
};
//==============================================================
// PALETTE
//==============================================================
// In this demo we'll be using one palette per sprite,
// so we've added a palette count to upload the correct number
// of entries.
//==============================================================
static const uint16_t palette[SIZE_PALETTE_W * PALETTE_COUNT] = {
// Palette for sprite 1
0x0000,
0x0020,
0x0EEE,
0x00AC,
0x02EA,
0x00EE,
0x0008,
0x000C,
0x000A,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
// Palette for sprite 2
0x0000,
0x0004,
0x0226,
0x0040,
0x0446,
0x0262,
0x0662,
0x004A,
0x0468,
0x0882,
0x006C,
0x0202,
0x04A0,
0x0AC2,
0x06AE,
0x02EC,
};
//==============================================================
// SPRITE TILES
//==============================================================
// The sprite graphics tiles. Too big to paste in here, so we'll
// include them from external files at the bottom of the ROM.
//
// If your tile data is in binary format rather than text, use
// INCBIN instead of INCLUDE.
//==============================================================
extern const uint32_t sprite_tiles[TILE_COUNT * SIZE_TILE_L];
//==============================================================
// VRAM WRITE MACROS
//==============================================================
// Some utility macros to help generate addresses and commands for
// writing data to video memory, since they're tricky (and
// error prone) to calculate manually.
// The resulting command and address is written to the VDP's
// control port, ready to accept data in the data port.
//==============================================================
// Set the VRAM (video RAM) address to write to next
static inline void SetVRAMWrite(uint16_t addr)
{
*(volatile uint32_t*)VDP_CONTROL = VDP_CMD_VRAM_WRITE | ((addr & 0x3FFF) << 16) | (addr >> 14);
}
// Set the CRAM (colour RAM) address to write to next
static inline void SetCRAMWrite(uint16_t addr)
{
*(volatile uint32_t*)VDP_CONTROL = VDP_CMD_CRAM_WRITE | ((addr & 0x3FFF) << 16) | (addr >> 14);
}
// Set the VSRAM (vertical scroll RAM) address to write to next
static inline void SetVSRAMWrite(uint16_t addr)
{
*(volatile uint32_t*)VDP_CONTROL = VDP_CMD_VSRAM_WRITE | ((addr & 0x3FFF) << 16) | (addr >> 14);
}
//==============================================================
// SPRITE ATTRIBUTE MACRO
//==============================================================
// A macro to help build an entry in the Sprite Attribute
// Table, since manipulating structures and bit twiddling isn't
// the focus of this demo, and would make the code harder to
// read.
//==============================================================
// Proper game implementations would make use of a local SAT
// table in RAM and use DMA to transfer the table to VRAM each
// frame (which also allows us to use RAM like a "stream" to write
// this data more efficiently) but this is the best method for
// teaching the basics first.
//==============================================================
// Each sprite attribute entry is in the following format:
//
// Y coordinate 1 word - the Y coordinate on the sprite plane
// Dimensions bits 1 byte - bits describing the layout (1x1 tiles up to 4x4 tiles)
// Linked list next 1 byte - the index of the next sprite to draw, or 0 if end of list
// Prio/palette/flip 1 byte - the priority (bit 15), palette (bits 14-13),
// v/h flip (bits 12 and 11), and top 3 bits of the tile ID
// Tile ID bottom 1 byte - the bottom 8 bits of the tile ID
// X coordinate 1 word - the X coordinate on the sprite plane
//==============================================================
// Writes a sprite attribute structure to 4 registers, ready to write to VRAM
static struct SpriteStructure { uint16_t reg1, reg2, reg3, reg4; } BuildSpriteStructure(
uint16_t x_pos, // X pos on sprite plane
uint16_t y_pos, // Y pos on sprite plane
uint16_t dimension_bits, // Sprite tile dimensions (4 bits)
uint16_t next_id, // Next sprite index in linked list
uint16_t priority_bit, // Draw priority
uint16_t palette_id, // Palette index
uint16_t flip_x, // Flip horizontally
uint16_t flip_y, // Flip vertically
uint16_t tile_id) // First tile index
{
return (struct SpriteStructure){
.reg1 = y_pos,
.reg2 = (dimension_bits << 8) | next_id,
.reg3 = (priority_bit << 14) | (palette_id << 13) | (flip_x << 11) | (flip_y << 10) |
tile_id,
.reg4 = x_pos,
};
}
static void VDP_WriteTMSS(void)
{
// The TMSS (Trademark Security System) locks up the VDP if we don't
// write the string 'SEGA' to a special address. This was to discourage
// unlicensed developers, since doing this displays the "LICENSED BY SEGA
// ENTERPRISES LTD" message to screen (on Mega Drive models 1 and higher).
//
// First, we need to check if we're running on a model 1+, then write
// 'SEGA' to hardware address 0xA14000.
const uint8_t ver = (*HARDWARE_VER_ADDRESS) & 0x0f;
if (ver != 0) {
*TMSS_ADDRESS = TMSS_SIGNATURE;
}
// Check VDP
// Read VDP status register (hangs if no access)
*VDP_CONTROL;
}
static void VDP_LoadRegisters(void)
{
// To initialise the VDP, we write all of its initial register values from
// the table at the top of the file, using a loop.
//
// To write a register, we write a word to the control port.
// The top bit must be set to 1 (so 0x8000), bits 8-12 specify the register
// number to write to, and the bottom byte is the value to set.
//
// In binary:
// 100X XXXX YYYY YYYY
// X = register number
// Y = value to write
// Set VDP registers
for (size_t i = 0; i < sizeof(vdp_registers) / sizeof(*vdp_registers); i++) {
const uint16_t cmd = 0x8000; // 'Set register 0' command
const uint16_t reg_num = i << 8;
*VDP_CONTROL = cmd | reg_num | vdp_registers[i];
}
}
int main(void)
{
//==============================================================
// Initialise the Mega Drive
//==============================================================
// Write the TMSS signature (if a model 1+ Mega Drive)
VDP_WriteTMSS();
// Load the initial VDP registers
VDP_LoadRegisters();
//==============================================================
// Clear VRAM (video memory)
//==============================================================
// Setup the VDP to write to VRAM address 0x0000 (start of VRAM)
SetVRAMWrite(0x0000);
// Write 0's across all of VRAM
const size_t count = (0x00010000 / SIZE_WORD); // Loop counter = 64kb, in words
for (size_t i = 0; i < count; i++) {
*VDP_DATA = 0x0; // Write a 0x0000 (word size) to VRAM
}
//==============================================================
// Write the palette to CRAM (colour memory)
//==============================================================
// Setup the VDP to write to CRAM address 0x0000 (first palette)
SetCRAMWrite(0x0000);
// Write the palettes to CRAM
//
// This time we're writing multiple palettes, so multiply the word count
// by the palette count (and don't forget the -1 for the loop counter).
for (size_t i = 0; i < PALETTE_COUNT * SIZE_PALETTE_W; i++) {
*VDP_DATA = palette[i]; // Write palette entry
}
//==============================================================
// Write the sprite tiles to VRAM
//==============================================================
// Setup the VDP to write to VRAM address 0x0020 (the address of the first sprite tile, index 1)
//
// We need to leave the first tile blank (we cleared VRAM, so it should be all 0's) for
// planes A and B to display, so skip the first tile (offset address by size_tile_b).
SetVRAMWrite(VRAM_ADDR_TILES + SIZE_TILE_B);
// Write the font glyph tiles to VRAM
for (size_t i = 0; i < TILE_COUNT * SIZE_TILE_L; i++) {
// Write tile line (4 bytes per line), and post-increment address
*(volatile uint32_t*)VDP_DATA = sprite_tiles[i];
}
//==============================================================
// Set up the Sprite Attribute Table (SAT)
//==============================================================
// The Sprite Attribute Table is a table of sprites to draw.
// Each entry in the table describes the first tile ID, the number
// of tiles to draw (and their layout), the X and Y position
// (on the 512x512 sprite plane), the palette to draw with, a
// priority flag, and X/Y flipping flags.
//
// Sprites can be layed out in these tile dimensions:
//
// 1x1 (1 tile) - 0000
// 1x2 (2 tiles) - 0001
// 1x3 (3 tiles) - 0010
// 1x4 (4 tiles) - 0011
// 2x1 (2 tiles) - 0100
// 2x2 (4 tiles) - 0101
// 2x3 (6 tiles) - 0110
// 2x4 (8 tiles) - 0111
// 3x1 (3 tiles) - 1000
// 3x2 (6 tiles) - 1001
// 3x3 (9 tiles) - 1010
// 3x4 (12 tiles)- 1011
// 4x1 (4 tiles) - 1100
// 4x2 (8 tiles) - 1101
// 4x3 (12 tiles)- 1110
// 4x4 (16 tiles)- 1111
//
// The tiles are layed out in COLUMN MAJOR, rather than planes A and B
// which are row major. Tiles within a sprite cannot be reused (since it
// only accepts a starting tile and a count/layout) so the whole sprite
// needs uploading to VRAM in one consecutive chunk, even if some tiles
// are duplicates.
//
// The X/Y flipping flags take the layout into account, you don't need
// to re-adjust the layout, position, or tile IDs to flip the entire
// sprite as a whole.
//
// There are 64 entries in the table, but the number of them drawn,
// and the order in which they're processed, is determined by a linked
// list. Each sprite entry has an index to the NEXT sprite to be drawn.
// If this index is 0, the list ends, and the VDP won't draw any more
// sprites this frame.
// Start writing to the sprite attribute table in VRAM
SetVRAMWrite(VRAM_ADDR_SPRITE_TABLE);
//==============================================================
// Set up sprite 1
// Write all values into registers first to make it easier. We
// write to VRAM one word at a time (auto-increment is set to 2
// in VDP register 0xF), so we'll assign each word to a register.
//
// Since bit twiddling and manipulating structures isn't the focus of
// this sample, we have a macro to simplify this part.
// Position: sprite_1_start_pos_x,sprite_1_start_pos_y
// Dimensions: 2x2 tiles (8 tiles total) = 0101 in binary (see table above)
// Next link: sprite index 1 is next to be processed
// Priority: 0
// Palette id: 0
// Flip X: 0
// Flip Y: 0
// Tile id: tile_id_sprite_1
const struct SpriteStructure sprite1 = BuildSpriteStructure(
SPRITE_1_START_POS_X, SPRITE_1_START_POS_Y, 5, 1, 0, 0, 0, 0, TILE_ID_SPRITE_1);
// Write the entire sprite attribute structure to the sprite table
*VDP_DATA = sprite1.reg1;
*VDP_DATA = sprite1.reg2;
*VDP_DATA = sprite1.reg3;
*VDP_DATA = sprite1.reg4;
//==============================================================
// Set up sprite 2
// Position: sprite_2_start_pos_x,sprite_2_start_pos_y
// Dimensions: 4x3 tiles (16 tiles total) = 1110 in binary (see table above)
// Next link: sprite index 0 (end of linked list)
// Priority: 0
// Palette id: 1
// Flip X: 0
// Flip Y: 0
// Tile id: tile_id_sprite_2
const struct SpriteStructure sprite2 = BuildSpriteStructure(
SPRITE_2_START_POS_X, SPRITE_2_START_POS_Y, 0xe, 0, 0, 1, 0, 0, TILE_ID_SPRITE_2);
// Write the entire sprite attribute structure to the sprite table
*VDP_DATA = sprite2.reg1;
*VDP_DATA = sprite2.reg2;
*VDP_DATA = sprite2.reg3;
*VDP_DATA = sprite2.reg4;
//==============================================================
// Intitialise variables in RAM
//==============================================================
*RAM_SPRITE_1_POS_X = SPRITE_1_START_POS_X;
*RAM_SPRITE_1_POS_Y = SPRITE_1_START_POS_Y;
*RAM_SPRITE_2_POS_X = SPRITE_2_START_POS_X;
*RAM_SPRITE_2_POS_Y = SPRITE_2_START_POS_Y;
//==============================================================
// Initialise status register and set interrupt level.
// This begins firing vertical and horizontal interrupts.
//
// Since the vinterrupt does something meaningful in this
// demo, we start this AFTER setting up the VDP to draw and
// intialising the variables in RAM.
//==============================================================
asm inline volatile (" move.w #0x2300, %sr");
// Finished!
//==============================================================
// Loop forever
//==============================================================
// This loops forever, effectively ending our main routine,
// but the VDP will continue to run of its own accord and
// will still fire vertical and horizontal interrupts (which is
// where our update code is), so the demo continues to run.
//
// For a game, it would be better to use this loop for processing
// input and game code, and wait here until next vblank before
// looping again. We only use vinterrupt for updates in this demo
// for simplicity (because we don't yet have any timing code).
while (1);
}
//==============================================================
// CODE ENTRY POINT
//==============================================================
// The "main()" function. Your code starts here. Once the CPU
// has finished initialising, it will jump to this entry point
// (specified in the vector table at the top of the file).
//==============================================================
__attribute__((interrupt)) void CPU_EntryPoint(void)
{
main();
}
// Vertical interrupt - run once per frame (50hz in PAL, 60hz in NTSC)
__attribute__((interrupt)) void INT_VInterrupt(void)
{
// Fetch current sprite coordinates from RAM
//
// Animate them (x/y coords are 9 bits, so this
// wraps around the whole 512x512 sprite plane)
//
// Store updated values back in RAM for next frame
*RAM_SPRITE_1_POS_X += SPRITE_1_MOVE_SPEED_X;
*RAM_SPRITE_1_POS_Y += SPRITE_1_MOVE_SPEED_Y;
*RAM_SPRITE_2_POS_X += SPRITE_2_MOVE_SPEED_X;
*RAM_SPRITE_2_POS_Y += SPRITE_2_MOVE_SPEED_Y;
// Write updated coordinates to the Sprite Attribute Table in VRAM.
// Each entry is 8 bytes in size, so sprite 1 is at table+0x0000,
// and sprite 2 is at table+0x0008.
//
// The Y coord is the 1st word in the structure, and the X coord is
// the 4th. As already noted, there are cleaner ways to do this,
// like storing the tables in RAM and copying them via DMA every
// frame, but that's beyond the focus of this sample.
// Sprite 1's Y coordinate is at table+0x0000
SetVRAMWrite(VRAM_ADDR_SPRITE_TABLE + 0x0000);
*VDP_DATA = *RAM_SPRITE_1_POS_Y;
// Sprite 1's X coordinate is at table+0x0006
SetVRAMWrite(VRAM_ADDR_SPRITE_TABLE + 0x0006);
*VDP_DATA = *RAM_SPRITE_1_POS_X;
// Sprite 2's Y coordinate is at table+0x0008
SetVRAMWrite(VRAM_ADDR_SPRITE_TABLE + 0x0008);
*VDP_DATA = *RAM_SPRITE_2_POS_Y;
// Sprite 2's X coordinate is at table+0x000E
SetVRAMWrite(VRAM_ADDR_SPRITE_TABLE + 0x000e);
*VDP_DATA = *RAM_SPRITE_2_POS_X;
}
|