1 /*
2  * vicii-snapshot.c - Snapshot functionality for the MOS 6569 (VIC-II)
3  * emulation.
4  *
5  * Written by
6  *  Ettore Perazzoli <ettore@comm2000.it>
7  *  Andreas Boose <viceteam@t-online.de>
8  *
9   * DTV sections written by
10  *  Hannu Nuotio <hannu.nuotio@tut.fi>
11  *  Daniel Kahlin <daniel@kahlin.net>
12  *
13  * This file is part of VICE, the Versatile Commodore Emulator.
14  * See README for copyright notice.
15  *
16  *  This program is free software; you can redistribute it and/or modify
17  *  it under the terms of the GNU General Public License as published by
18  *  the Free Software Foundation; either version 2 of the License, or
19  *  (at your option) any later version.
20  *
21  *  This program is distributed in the hope that it will be useful,
22  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
23  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  *  GNU General Public License for more details.
25  *
26  *  You should have received a copy of the GNU General Public License
27  *  along with this program; if not, write to the Free Software
28  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
29  *  02111-1307  USA.
30  *
31  */
32 
33 #include "vice.h"
34 
35 #include <stdio.h>
36 
37 #include "alarm.h"
38 #include "interrupt.h"
39 #include "log.h"
40 #include "mem.h"
41 #include "raster-sprite-status.h"
42 #include "raster-sprite.h"
43 #include "snapshot.h"
44 #include "types.h"
45 #include "vicii-irq.h"
46 #include "vicii-snapshot.h"
47 #include "vicii-sprites.h"
48 #include "vicii.h"
49 #include "viciitypes.h"
50 #include "vicii-mem.h"
51 
52 
53 /* Make sure all the VIC-II alarms are removed.  This just makes it easier to
54    write functions for loading snapshot modules in other video chips without
55    caring that the VIC-II alarms are dispatched when they really shouldn't
56    be.  */
vicii_snapshot_prepare(void)57 void vicii_snapshot_prepare(void)
58 {
59     vicii.fetch_clk = CLOCK_MAX;
60     alarm_unset(vicii.raster_fetch_alarm);
61     vicii.draw_clk = CLOCK_MAX;
62     alarm_unset(vicii.raster_draw_alarm);
63     vicii.raster_irq_clk = CLOCK_MAX;
64     alarm_unset(vicii.raster_irq_alarm);
65 }
66 
67 
68 /*
69 
70    This is the format of the VIC-II snapshot module.
71 
72    Name               Type   Size   Description
73 
74    AllowBadLines      BYTE   1      flag: if true, bad lines can happen
75    BadLine            BYTE   1      flag: this is a bad line
76    Blank              BYTE   1      flag: draw lines in border color
77    ColorBuf           BYTE   40     character memory buffer (loaded at bad line)
78    IdleState          BYTE   1      flag: idle state enabled
79    LPTrigger          BYTE   1      flag: light pen has been triggered
80    LPX                BYTE   1      light pen X
81    LPY                BYTE   1      light pen Y
82    MatrixBuf          BYTE   40     video matrix buffer (loaded at bad line)
83    NewSpriteDmaMask   BYTE   1      value for SpriteDmaMask after drawing
84                                     sprites
85    RamBase            DWORD  1      pointer to the start of RAM seen by the VIC
86    RasterCycle        BYTE   1      current vicii.raster cycle
87    RasterLine         WORD   1      current vicii.raster line
88    Registers          BYTE   80     VIC-II registers
89    DTVstuff           x      x      DTV stuff goes here
90    SbCollMask         BYTE   1      sprite-background collisions so far
91    SpriteDmaMask      BYTE   1      sprites having DMA turned on
92    SsCollMask         BYTE   1      sprite-sprite collisions so far
93    VBank              BYTE   1      location of memory bank
94    Vc                 WORD   1      internal VIC-II counter
95    VcAdd              BYTE   1      value to add to Vc at the end of this line
96                                     (vicii.mem_counter_inc)
97    VcBase             WORD   1      internal VIC-II memory pointer
98    VideoInt           BYTE   1      status of VIC-II IRQ (vicii.irq_status)
99 
100    [Sprite section: (repeat 8 times)]
101 
102    SpriteXMemPtr      BYTE   1      sprite memory pointer
103    SpriteXMemPtrInc   BYTE   1      value to add to the MemPtr after fetch
104    SpriteXExpFlipFlop BYTE   1      sprite expansion flip-flop
105 
106    [Alarm section]
107    FetchEventTick     DWORD  1      ticks for the next "fetch" (DMA) event
108    FetchEventType     BYTE   1      type of event (0: matrix, 1: sprite check, 2: sprite fetch)
109 
110  */
111 
112 static char snap_module_name[] = "VIC-II";
113 #define SNAP_MAJOR 1
114 #define SNAP_MINOR 1
115 
vicii_snapshot_write_module(snapshot_t * s)116 int vicii_snapshot_write_module(snapshot_t *s)
117 {
118     int i;
119     snapshot_module_t *m;
120 
121     /* FIXME: Dispatch all events?  */
122 
123     m = snapshot_module_create (s, snap_module_name, SNAP_MAJOR, SNAP_MINOR);
124     if (m == NULL) {
125         return -1;
126     }
127 
128     if (0
129         /* AllowBadLines */
130         || SMW_B(m, (uint8_t)vicii.allow_bad_lines) < 0
131         /* BadLine */
132         || SMW_B(m, (uint8_t)vicii.bad_line) < 0
133         /* Blank */
134         || SMW_B(m, (uint8_t)vicii.raster.blank_enabled) < 0
135         /* ColorBuf */
136         || SMW_BA(m, vicii.cbuf, 40) < 0
137         /* IdleState */
138         || SMW_B(m, (uint8_t)vicii.idle_state) < 0
139         /* LPTrigger */
140         || SMW_B(m, (uint8_t)vicii.light_pen.triggered) < 0
141         /* LPX */
142         || SMW_B(m, (uint8_t)vicii.light_pen.x) < 0
143         /* LPY */
144         || SMW_B(m, (uint8_t)vicii.light_pen.y) < 0
145         /* MatrixBuf */
146         || SMW_BA(m, vicii.vbuf, 40) < 0
147         /* NewSpriteDmaMask */
148         || SMW_B(m, vicii.raster.sprite_status->new_dma_msk) < 0
149         /* RamBase */
150         || SMW_DW(m, (uint32_t)(vicii.ram_base_phi1 - mem_ram)) < 0
151         /* RasterCycle */
152         || SMW_B(m, (uint8_t)(VICII_RASTER_CYCLE(maincpu_clk))) < 0
153         /* RasterLine */
154         || SMW_W(m, (uint16_t)(VICII_RASTER_Y(maincpu_clk))) < 0) {
155         goto fail;
156     }
157 
158     for (i = 0; i < 0x50; i++) {
159         /* Registers */
160         if (SMW_B(m, vicii.regs[i]) < 0) {
161             goto fail;
162         }
163     }
164 
165     if (0
166         /* DTV stuff */
167         || SMW_DW(m, (uint32_t)vicii.counta) < 0
168         || SMW_DW(m, (uint32_t)vicii.counta_mod) < 0
169         || SMW_DW(m, (uint32_t)vicii.counta_step) < 0
170         || SMW_DW(m, (uint32_t)vicii.countb) < 0
171         || SMW_DW(m, (uint32_t)vicii.countb_mod) < 0
172         || SMW_DW(m, (uint32_t)vicii.countb_step) < 0
173         || SMW_DW(m, (uint32_t)vicii.extended_enable) < 0
174         || SMW_DW(m, (uint32_t)vicii.extended_lockout) < 0
175         || SMW_DW(m, (uint32_t)vicii.badline_disable) < 0
176         || SMW_DW(m, (uint32_t)vicii.colorfetch_disable) < 0
177         || SMW_DW(m, (uint32_t)vicii.overscan) < 0
178         || SMW_DW(m, (uint32_t)vicii.high_color) < 0
179         || SMW_DW(m, (uint32_t)vicii.border_off) < 0
180         || SMW_DW(m, (uint32_t)vicii.raster_irq_offset) < 0
181         || SMW_DW(m, (uint32_t)vicii.raster_irq_prevent) < 0
182         || SMW_BA(m, vicii.dtvpalette, 256) < 0
183         /* SbCollMask */
184         || SMW_B(m, (uint8_t)vicii.sprite_background_collisions) < 0
185         /* SpriteDmaMask */
186         || SMW_B(m, (uint8_t)vicii.raster.sprite_status->dma_msk) < 0
187         /* SsCollMask */
188         || SMW_B(m, (uint8_t)vicii.sprite_sprite_collisions) < 0
189         /* VBank */
190         || SMW_W(m, (uint16_t)vicii.vbank_phi1) < 0
191         /* Vc */
192         || SMW_W(m, (uint16_t)vicii.mem_counter) < 0
193         /* VcInc */
194         || SMW_B(m, (uint8_t)vicii.mem_counter_inc) < 0
195         /* VcBase */
196         || SMW_W(m, (uint16_t)vicii.memptr) < 0
197         /* VideoInt */
198         || SMW_B(m, (uint8_t)vicii.irq_status) < 0) {
199         goto fail;
200     }
201 
202     for (i = 0; i < 8; i++) {
203         if (0
204             /* SpriteXMemPtr */
205             || SMW_B(m, (uint8_t)vicii.raster.sprite_status->sprites[i].memptr) < 0
206             /* SpriteXMemPtrInc */
207             || SMW_B(m, (uint8_t)vicii.raster.sprite_status->sprites[i].memptr_inc) < 0
208             /* SpriteXExpFlipFlop */
209             || SMW_B(m, (uint8_t)vicii.raster.sprite_status->sprites[i].exp_flag) < 0) {
210             goto fail;
211         }
212     }
213 
214     if (0
215         /* FetchEventTick */
216         || SMW_DW(m, vicii.fetch_clk - maincpu_clk) < 0
217         /* FetchEventType */
218         || SMW_B(m, (uint8_t)vicii.fetch_idx) < 0) {
219         goto fail;
220     }
221 
222     /* Added in version 1.1 of the snapshot module */
223     /* using "ram_base-ram" is F***ing bullshit - what when external memory
224        is not mapped anywhere in ram[]? We should rather use some more generic
225        configuration info. But as we use it above in V1.0... :-(
226        AF 16jan2001 */
227     if (0
228         /* RamBase */
229         || SMW_DW(m, (uint32_t)(vicii.ram_base_phi2 - mem_ram)) < 0
230         /* VBank */
231         || SMW_W(m, (uint16_t)vicii.vbank_phi2) < 0) {
232         goto fail;
233     }
234 
235     return snapshot_module_close(m);
236 
237 fail:
238     if (m != NULL) {
239         snapshot_module_close(m);
240     }
241 
242     return -1;
243 }
244 
vicii_snapshot_read_module(snapshot_t * s)245 int vicii_snapshot_read_module(snapshot_t *s)
246 {
247     uint8_t major_version, minor_version;
248     int i;
249     snapshot_module_t *m;
250 
251     m = snapshot_module_open(s, snap_module_name,
252                              &major_version, &minor_version);
253     if (m == NULL) {
254         return -1;
255     }
256 
257     if (snapshot_version_is_bigger(major_version, minor_version, SNAP_MAJOR, SNAP_MINOR)) {
258         log_error(vicii.log,
259                   "Snapshot module version (%d.%d) newer than %d.%d.",
260                   major_version, minor_version,
261                   SNAP_MAJOR, SNAP_MINOR);
262         goto fail;
263     }
264 
265     /* FIXME: initialize changes?  */
266 
267     if (0
268         /* AllowBadLines */
269         || SMR_B_INT(m, &vicii.allow_bad_lines) < 0
270         /* BadLine */
271         || SMR_B_INT(m, &vicii.bad_line) < 0
272         /* Blank */
273         || SMR_B_INT(m, &vicii.raster.blank_enabled) < 0
274         /* ColorBuf */
275         || SMR_BA(m, vicii.cbuf, 40) < 0
276         /* IdleState */
277         || SMR_B_INT(m, &vicii.idle_state) < 0
278         /* LPTrigger */
279         || SMR_B_INT(m, &vicii.light_pen.triggered) < 0
280         /* LPX */
281         || SMR_B_INT(m, &vicii.light_pen.x) < 0
282         /* LPY */
283         || SMR_B_INT(m, &vicii.light_pen.y) < 0
284         /* MatrixBuf */
285         || SMR_BA(m, vicii.vbuf, 40) < 0
286         /* NewSpriteDmaMask */
287         || SMR_B(m, &vicii.raster.sprite_status->new_dma_msk) < 0) {
288         goto fail;
289     }
290 
291     {
292         uint32_t RamBase;
293 
294         if (SMR_DW(m, &RamBase) < 0) {
295             goto fail;
296         }
297         vicii.ram_base_phi1 = mem_ram + RamBase;
298     }
299 
300     /* Read the current raster line and the current raster cycle.  As they
301        are a function of `clk', this is just a sanity check.  */
302     {
303         uint16_t RasterLine;
304         uint8_t RasterCycle;
305 
306         if (SMR_B(m, &RasterCycle) < 0
307             || SMR_W(m, &RasterLine) < 0) {
308             goto fail;
309         }
310 
311         if (RasterCycle != (uint8_t)VICII_RASTER_CYCLE(maincpu_clk)) {
312             log_error(vicii.log,
313                       "Not matching raster cycle (%d) in snapshot; should be %u.",
314                       RasterCycle, VICII_RASTER_CYCLE(maincpu_clk));
315             goto fail;
316         }
317 
318         if (RasterLine != (uint16_t)VICII_RASTER_Y(maincpu_clk)) {
319             log_error(vicii.log,
320                       "VIC-II: Not matching raster line (%d) in snapshot; should be %u.",
321                       RasterLine, VICII_RASTER_Y(maincpu_clk));
322             goto fail;
323         }
324     }
325 
326     for (i = 0; i < 0x50; i++) {
327         if (SMR_B(m, &vicii.regs[i]) < 0 /* Registers */) {
328             goto fail;
329         }
330     }
331 
332     if (0
333         /* DTV stuff */
334         || SMR_DW_INT(m, &vicii.counta) < 0
335         || SMR_DW_INT(m, &vicii.counta_mod) < 0
336         || SMR_DW_INT(m, &vicii.counta_step) < 0
337         || SMR_DW_INT(m, &vicii.countb) < 0
338         || SMR_DW_INT(m, &vicii.countb_mod) < 0
339         || SMR_DW_INT(m, &vicii.countb_step) < 0
340         || SMR_DW_INT(m, (int *)&vicii.extended_enable) < 0
341         || SMR_DW_INT(m, (int *)&vicii.extended_lockout) < 0
342         || SMR_DW_INT(m, (int *)&vicii.badline_disable) < 0
343         || SMR_DW_INT(m, (int *)&vicii.colorfetch_disable) < 0
344         || SMR_DW_INT(m, (int *)&vicii.overscan) < 0
345         || SMR_DW_INT(m, (int *)&vicii.high_color) < 0
346         || SMR_DW_INT(m, (int *)&vicii.border_off) < 0
347         || SMR_DW_INT(m, &vicii.raster_irq_offset) < 0
348         || SMR_DW_INT(m, &vicii.raster_irq_prevent) < 0
349         || SMR_BA(m, vicii.dtvpalette, 256) < 0
350         /* SbCollMask */
351         || SMR_B(m, &vicii.sprite_background_collisions) < 0
352         /* SpriteDmaMask */
353         || SMR_B(m, &vicii.raster.sprite_status->dma_msk) < 0
354         /* SsCollMask */
355         || SMR_B(m, &vicii.sprite_sprite_collisions) < 0
356         /* VBank */
357         || SMR_W_INT(m, &vicii.vbank_phi1) < 0
358         /* Vc */
359         || SMR_W_INT(m, &vicii.mem_counter) < 0
360         /* VcInc */
361         || SMR_B_INT(m, &vicii.mem_counter_inc) < 0
362         /* VcBase */
363         || SMR_W_INT(m, &vicii.memptr) < 0
364         /* VideoInt */
365         || SMR_B_INT(m, &vicii.irq_status) < 0) {
366         goto fail;
367     }
368 
369     for (i = 0; i < 8; i++) {
370         if (0
371             /* SpriteXMemPtr */
372             || SMR_B_INT(m, &vicii.raster.sprite_status->sprites[i].memptr) < 0
373             /* SpriteXMemPtrInc */
374             || SMR_B_INT(m, &vicii.raster.sprite_status->sprites[i].memptr_inc) < 0
375             /* SpriteXExpFlipFlop */
376             || SMR_B_INT(m, &vicii.raster.sprite_status->sprites[i].exp_flag) < 0
377             ) {
378             goto fail;
379         }
380     }
381 
382     /* FIXME: Recalculate alarms and derived values.  */
383 #if 1
384     if (vicii.raster_irq_prevent) {
385         vicii.raster_irq_clk = CLOCK_MAX;
386         alarm_unset(vicii.raster_irq_alarm);
387     } else {
388         /*
389             We cannot use vicii_irq_set_raster_line as this would delay
390             an alarm on line 0 for one frame
391         */
392         unsigned int line = vicii.regs[0x12] | ((vicii.regs[0x11] & 0x80) << 1);
393 
394         if (line < (unsigned int)vicii.screen_height) {
395             vicii.raster_irq_clk = (VICII_LINE_START_CLK(maincpu_clk)
396                                     + VICII_RASTER_IRQ_DELAY - INTERRUPT_DELAY
397                                     + (vicii.cycles_per_line * line));
398             vicii.raster_irq_clk += vicii.raster_irq_offset;
399             /* Raster interrupts on line 0 are delayed by 1 cycle.  */
400             if (line == 0) {
401                 vicii.raster_irq_clk++;
402             }
403 
404             alarm_set(vicii.raster_irq_alarm, vicii.raster_irq_clk);
405         } else {
406             vicii.raster_irq_clk = CLOCK_MAX;
407             alarm_unset(vicii.raster_irq_alarm);
408         }
409         vicii.raster_irq_line = line;
410     }
411 
412 #else
413     vicii_irq_set_raster_line(vicii.regs[0x12]
414                               | ((vicii.regs[0x11] & 0x80) << 1));
415 #endif
416 
417     /* compatibility with older versions */
418     vicii.ram_base_phi2 = vicii.ram_base_phi1;
419     vicii.vbank_phi2 = vicii.vbank_phi1;
420 
421     vicii_update_memory_ptrs(VICII_RASTER_CYCLE(maincpu_clk));
422 
423     /* Update sprite parameters.  We had better do this manually, or the
424        VIC-II emulation could be quite upset.  */
425     {
426         uint8_t msk;
427 
428         for (i = 0, msk = 0x1; i < 8; i++, msk <<= 1) {
429             raster_sprite_t *sprite;
430             int tmp;
431 
432             sprite = vicii.raster.sprite_status->sprites + i;
433 
434             /* X/Y coordinates.  */
435             tmp = vicii.regs[i * 2] + ((vicii.regs[0x10] & msk) ? 0x100 : 0);
436 
437             /* (-0xffff makes sure it's updated NOW.) */
438             vicii_sprites_set_x_position(i, tmp, -0xffff);
439 
440             sprite->y = (int)vicii.regs[i * 2 + 1];
441             sprite->x_expanded = (int)(vicii.regs[0x1d] & msk);
442             sprite->y_expanded = (int)(vicii.regs[0x17] & msk);
443             sprite->multicolor = (int)(vicii.regs[0x1c] & msk);
444             sprite->in_background = (int)(vicii.regs[0x1b] & msk);
445             sprite->color = (int) vicii.dtvpalette[vicii.regs[0x27 + i] & 0xf];
446             sprite->dma_flag = (int)(vicii.raster.sprite_status->new_dma_msk & msk);
447         }
448     }
449 
450     vicii.sprite_fetch_msk = vicii.raster.sprite_status->new_dma_msk;
451     vicii.sprite_fetch_clk = VICII_LINE_START_CLK(maincpu_clk)
452                              + vicii.sprite_fetch_cycle
453                              - vicii.cycles_per_line;
454 
455     /* calculate the sprite_fetch_idx */
456     {
457         const vicii_sprites_fetch_t *sf;
458 
459         sf = vicii_sprites_fetch_table[vicii.sprite_fetch_msk];
460         i = 0;
461         while (sf[i].cycle >= 0 && sf[i].cycle + vicii.sprite_fetch_cycle <= vicii.cycles_per_line)
462         {
463             i++;
464         }
465         vicii.sprite_fetch_idx = i;
466     }
467 
468     vicii.raster.xsmooth = vicii.regs[0x16] & 0x7;
469     vicii.raster.sprite_xsmooth = vicii.regs[0x16] & 0x7;
470     vicii.raster.ysmooth = vicii.regs[0x11] & 0x7;
471     vicii.raster.current_line = VICII_RASTER_Y(maincpu_clk); /* FIXME? */
472 
473     vicii.raster.sprite_status->visible_msk = vicii.regs[0x15];
474 
475     /* Update colors.  */
476     vicii.raster.border_color = vicii.dtvpalette[vicii.regs[0x20]];
477     vicii.raster.background_color = vicii.dtvpalette[vicii.regs[0x21]];
478     vicii.ext_background_color[0] = vicii.dtvpalette[vicii.regs[0x22]];
479     vicii.ext_background_color[1] = vicii.dtvpalette[vicii.regs[0x23]];
480     vicii.ext_background_color[2] = vicii.dtvpalette[vicii.regs[0x24]];
481     vicii.raster.sprite_status->mc_sprite_color_1 = vicii.dtvpalette[vicii.regs[0x25] & 0xf];
482     vicii.raster.sprite_status->mc_sprite_color_2 = vicii.dtvpalette[vicii.regs[0x26] & 0xf];
483 
484     vicii.raster.blank = !(vicii.regs[0x11] & 0x10);
485 
486     if (VICII_IS_ILLEGAL_MODE(vicii.raster.video_mode)) {
487         vicii.raster.idle_background_color = 0;
488         vicii.force_black_overscan_background_color = 1;
489     } else {
490         vicii.raster.idle_background_color
491             = vicii.raster.background_color;
492         vicii.force_black_overscan_background_color = 0;
493     }
494 
495     if (vicii.regs[0x11] & 0x8) {
496         vicii.raster.display_ystart = vicii.row_25_start_line;
497         vicii.raster.display_ystop = vicii.row_25_stop_line;
498     } else {
499         vicii.raster.display_ystart = vicii.row_24_start_line;
500         vicii.raster.display_ystop = vicii.row_24_stop_line;
501     }
502 
503     if (vicii.regs[0x16] & 0x8) {
504         vicii.raster.display_xstart = VICII_40COL_START_PIXEL;
505         vicii.raster.display_xstop = VICII_40COL_STOP_PIXEL;
506     } else {
507         vicii.raster.display_xstart = VICII_38COL_START_PIXEL;
508         vicii.raster.display_xstop = VICII_38COL_STOP_PIXEL;
509     }
510 
511     /* `vicii.raster.draw_idle_state', `vicii.raster.open_right_border' and
512        `vicii.raster.open_left_border' should be needed, but they would only
513        affect the current vicii.raster line, and would not cause any
514        difference in timing.  So who cares.  */
515 
516     /* FIXME: `vicii.ycounter_reset_checked'?  */
517     /* FIXME: `vicii.force_display_state'?  */
518 
519     vicii.memory_fetch_done = 0; /* FIXME? */
520 
521     vicii_update_video_mode(VICII_RASTER_CYCLE(maincpu_clk));
522 
523     vicii_store(0x3c, (uint8_t)vicii.regs[0x3c]);
524 
525     vicii.draw_clk = maincpu_clk + (vicii.draw_cycle - VICII_RASTER_CYCLE(maincpu_clk));
526     vicii.last_emulate_line_clk = vicii.draw_clk - vicii.cycles_per_line;
527     alarm_set(vicii.raster_draw_alarm, vicii.draw_clk);
528 
529     {
530         uint32_t dw;
531         uint8_t b;
532 
533         if (0
534             || SMR_DW(m, &dw) < 0  /* FetchEventTick */
535             || SMR_B(m, &b) < 0    /* FetchEventType */
536             ) {
537             goto fail;
538         }
539 
540         vicii.fetch_clk = maincpu_clk + dw;
541         vicii.fetch_idx = b;
542 
543         alarm_set(vicii.raster_fetch_alarm, vicii.fetch_clk);
544     }
545 
546     if (vicii.irq_status & 0x80) {
547         interrupt_restore_irq(maincpu_int_status, vicii.int_num, 1);
548     }
549 
550     /* added in version 1.1 of snapshot format */
551     if (minor_version > 0) {
552         uint32_t RamBase;
553 
554         if (0
555             || SMR_DW(m, &RamBase) < 0
556             || SMR_W_INT(m, &vicii.vbank_phi2) < 0 /* VBank */
557             ) {
558             goto fail;
559         }
560         vicii.ram_base_phi2 = mem_ram + RamBase;
561 
562         vicii_update_memory_ptrs(VICII_RASTER_CYCLE(maincpu_clk));
563     }
564 
565     raster_force_repaint(&vicii.raster);
566     snapshot_module_close(m);
567     return 0;
568 
569 fail:
570     if (m != NULL) {
571         snapshot_module_close(m);
572     }
573     return -1;
574 }
575