-
Notifications
You must be signed in to change notification settings - Fork 0
/
x_scroll.ras
286 lines (222 loc) · 6.83 KB
/
x_scroll.ras
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
//
// This program does a right to left x-scroll using the standard double buffered approach.
// It uses a Uridium ship map, those are 512 chars wide by 17 chars high. Therefore on a text
// screen there are 4 blank rows (or 4 non-moving starfield rows in the actual game), then 17
// rows of ship chars, then another 4 blank rows. The scroll routine only copies\shifts the
// 17 ship rows between front and back for more speed.
//
// The charset is dumped from one of the CharPad examples.
//
// The general approach is from: http://1amstudios.com/2014/12/07/c64-smooth-scrolling
//
// In pseudocode terms described here: https://github.com/jeff-1amstudios/c64-smooth-scrolling
//
//
program x_scroll;
var
@define useKernal "0"
@define unroll 0 // -- Set to 1 to use unrolled loop in screen copy.
const start_colorcopy_line: byte = 65;
const begin_vblank_line : byte = 245;
const vic_colour_base : address = $d800;
const charset_base : address = $2000;
const map_height : byte = 17;
const map_width : integer = 512;
const map_base : address = $5000;
const screen_base : address = $3000;
const screen_backbuffer_base : address = screen_base + 1024;
charset : IncBin("resources/UridiumChars.bin", #charset_base);
map : IncBin("resources/UridiumMap.bin", #map_base);
from_ptr, to_ptr, screen_base_ptr, backbuffer_base_ptr, colour_base_ptr, map_ptr: pointer;
offset, map_column : integer = 0;
current_screen, i: byte = 0;
row, col, numlines, this_colour, this_char, startline : byte = 0;
scroll : byte = 7;
// Forward declarations.
interrupt irq_begin_vblank();
procedure screen_swap();
procedure copy_colors();
procedure swap_screens();
procedure DrawColumn39ToBack();
procedure copy_and_shift();
// Line 65
interrupt irq_line_65;
begin
StartIRQ(@useKernal);
//SetMultiColorMode();
if (scroll = 0) then
begin
//startline := 0;
//numlines := 12;
//copy_colors(); // Don't actually need for this example.
end;
RasterIRQ(irq_begin_vblank(), begin_vblank_line, @useKernal);
CloseIRQ();
end;
// ------------------------------------------------------------------------
// copy_colors()
//
// Copies an area the colour screen one step to the left.
//
// ------------------------------------------------------------------------
procedure copy_colors();
begin
row := 0;
while (row < numlines) do
begin
offset := (startline + row) * 40;
colour_base_ptr := #vic_colour_base + offset;
col := 1;
while (col < 40) do
begin
i := col - 1;
colour_base_ptr[i] := colour_base_ptr[col];
inc(col);
end;
colour_base_ptr += 40;
inc(row);
end;
end;
// ------------------------------------------------------------------------
// irq_begin_vblank
//
// The main interrupt at around the vblank area at the bottom of the screen.
// Scrolls the screen right to left using the pixel hardware scroll.
// Will either copy the top half of the screen to the back buffer shifted
// left one char, or the same the other direction, or will point the VIC
// at the other screen when the time comes, that being when the h/w scroll
// position has gone all the way to the left (<0)
// ------------------------------------------------------------------------
interrupt irq_begin_vblank;
begin
StartIRQ(@useKernal);
dec(scroll);
// 2's complement, >= 127 means < 0
if (scroll >= 127) then
begin
swap_screens();
end
else
begin
scrollx(scroll);
// Copy top half of char screen to back buffer.
if (scroll = 4) then
begin
startline := 4; // zero-based
numlines := 8; // one-based
copy_and_shift();
end;
// Copy bottom half of char screen to back buffer.
if (scroll = 2) then
begin
startline := 12;
numlines := 9;
copy_and_shift();
end;
end;
hideborderx(1);
RasterIrq(irq_line_65(), start_colorcopy_line, @useKernal);
CloseIRQ();
end;
// ------------------------------------------------------------------------
// copy_and_shift()
//
// Copies the current screen to the backbuffer screen but shifted left one
// char, leaving a row at the the right where a new slice of the map will be
// drawn in.
// ------------------------------------------------------------------------
procedure copy_and_shift();
begin
if (current_screen = 0) then
begin
from_ptr := #screen_base;
to_ptr := #screen_backbuffer_base;
end
else
begin
from_ptr := #screen_backbuffer_base;
to_ptr := #screen_base;
end;
// Move down to start row.
from_ptr += 1 + (startline * 40); // Start copying chars positions 1 .. 40 from front
// to char positions 0 .. 39 on back.
to_ptr += (startline * 40);
row := 0;
while (row < numlines) do
begin
@if unroll = 0
memcpyfast(from_ptr, 0, to_ptr, 39);
@else
memcpyunroll(from_ptr, 0, to_ptr, 39);
@endif
from_ptr += 40;
to_ptr += 40;
inc(row);
end;
end;
// ------------------------------------------------------------------------
// DrawColumn39FromMap
//
// Draws the next column of chars from the map at the rightmost column on
// the back buffer screen.
// ------------------------------------------------------------------------
procedure DrawColumn39FromMap();
begin
if (current_screen = 0) then
to_ptr := #screen_backbuffer_base
else
to_ptr := #screen_base;
// 4 blank rows, then 17 map rows, then 4 blank rows.
i := 1;
to_ptr += (4*40); // Start on screen row 4
map_ptr := #map_base + map_column;
fori i := 0 to 17 do
begin
to_ptr[39] := map_ptr[0];
to_ptr += 40;
map_ptr += map_width;
end;
inc(map_column);
if map_column = 254 then map_column := 0;
end;
// ------------------------------------------------------------------------
// swap_screens
//
// Flips the current screen between pointing at the front and back buffers.
// ------------------------------------------------------------------------
procedure swap_screens();
begin
DrawColumn39FromMap();
scroll := 7;
scrollx(scroll);
current_screen := (current_screen + 1) & 1;
if current_screen = 1 then
SetScreenLocation(screen_backbuffer_base)
else
SetScreenLocation(screen_base);
SetMultiColorMode();
//copy_colors(1);
end;
// ------------------------------------------------------------------------
// Main program.
// ------------------------------------------------------------------------
begin
PreventIrQ(); // System IRQs, not mine.
DisableCIAInterrupts();
screen_base_ptr := #screen_base;
backbuffer_base_ptr := #screen_backbuffer_base;
ClearScreen(1, #screen_base);
ClearScreen(1, #screen_backbuffer_base);
// Sort-of-correct paletter from the game Uridium.
screen_bg_col := black;
screen_fg_col := white;
screen_fg_col[1] := yellow;
screen_fg_col[2] := orange;
SetMultiColorMode();
current_screen := 0;
SetScreenLocation(screen_base);
SetMemoryConfig(1, 0, 0); // Do this last.
setcharsetlocation(#charset_base);
StartRasterChain(irq_line_65(), start_colorcopy_line, @useKernal);
Loop();
end.