/** \file c128mmu.c * \brief C128 memory management uint * * \author Andreas Boose * \author Marco van den Heuvel */ /* * This file is part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA. * */ #include "vice.h" #include #include "c128.h" #include "c128-resources.h" #include "c128fastiec.h" #include "c128mem.h" #include "c128memrom.h" #include "c128mmu.h" #include "c64cart.h" #include "c128cart.h" #include "cmdline.h" #include "functionrom.h" #include "interrupt.h" #include "keyboard.h" #include "keymap.h" #include "log.h" #include "maincpu.h" #include "mem.h" #include "monitor.h" #include "resources.h" #include "reu.h" #include "types.h" #include "vdc.h" #include "vicii.h" #include "viciitypes.h" #include "z80.h" #include "z80mem.h" /* #define MMU_DEBUG */ /* #define DEBUG_KEYS */ #ifdef DEBUG_KEYS #define DBGKEY(x) log_printf x #else #define DBGKEY(x) #endif #define NUM_CONFIGS64 32 #define NUM_CONFIGS128 256 #define NUM_CONFIGS (NUM_CONFIGS64+NUM_CONFIGS128) /* MMU register. */ uint8_t mmu[12]; /* latches for P0H and P1H */ static uint8_t p0h_latch, p1h_latch; /* State of the 40/80 column key (Resource value) */ static int mmu_column4080_key = -1; static int force_c64_mode_res = 0; static int force_c64_mode = 0; static int mmu_config64 = 0; /* Logging goes here. */ static log_t mmu_log = LOG_DEFAULT; /* ------------------------------------------------------------------------- */ /* resource handler for "C128ColumnKey" = 1 : 40 colums (key released: 0) = 0 : 80 colums (key pressed: 1) */ static int set_column4080_key(int val, void *param) { DBGKEY(("set_column4080_key %d", val)); if (mmu_column4080_key != val) { /* caution, the resource value is 1 when the key is not pressed (val = 0) */ keyboard_custom_key_set(KBD_CUSTOM_4080, val ? 0 : 1); val = keyboard_custom_key_get(KBD_CUSTOM_4080); if (val != 1) { val = 0; } /* caution, the resource value is 1 when the key is not pressed (val = 0) */ mmu_column4080_key = val ^ 1; DBGKEY(("set_column4080_key mmu_column4080_key:%d 40/80 column key: %s.", mmu_column4080_key, mmu_column4080_key ? "40cols" : "80cols")); } #ifdef HAS_SINGLE_CANVAS vdc_set_canvas_refresh(mmu_column4080_key ? 0 : 1); vicii_set_canvas_refresh(mmu_column4080_key ? 1 : 0); #endif return 0; } static int set_force_c64_mode(int val, void *param) { force_c64_mode_res = val ? 1 : 0; return 0; } static const resource_int_t resources_int[] = { { "C128ColumnKey", 1, RES_EVENT_SAME, NULL, &mmu_column4080_key, set_column4080_key, NULL }, { "Go64Mode", 0, RES_EVENT_SAME, NULL, &force_c64_mode_res, set_force_c64_mode, NULL }, RESOURCE_INT_LIST_END }; int mmu_resources_init(void) { return resources_register_int(resources_int); } static const cmdline_option_t cmdline_options[] = { { "-40col", SET_RESOURCE, CMDLINE_ATTRIB_NONE, NULL, NULL, "C128ColumnKey", (resource_value_t) 1, NULL, "Activate 40 column mode" }, { "-80col", SET_RESOURCE, CMDLINE_ATTRIB_NONE, NULL, NULL, "C128ColumnKey", (resource_value_t) 0, NULL, "Activate 80 column mode" }, { "-go64", SET_RESOURCE, CMDLINE_ATTRIB_NONE, NULL, NULL, "Go64Mode", (resource_value_t) 1, NULL, "Always switch to C64 mode on reset" }, { "+go64", SET_RESOURCE, CMDLINE_ATTRIB_NONE, NULL, NULL, "Go64Mode", (resource_value_t) 0, NULL, "Always switch to C128 mode on reset" }, CMDLINE_LIST_END }; int mmu_cmdline_options_init(void) { return cmdline_register_options(cmdline_options); } /* ------------------------------------------------------------------------- */ static int mmu_is_in_shared_ram(uint16_t address) { unsigned int shared_size; if (mmu[6] & 3) { shared_size = 2048U << (mmu[6] & 3); } else { shared_size = 1024; } if ((mmu[6] & 4) && address < shared_size) { return 1; } if ((mmu[6] & 8) && address >= shared_size) { return 1; } return 0; } static uint8_t mmu_is_valid_ram(uint8_t page, uint8_t bank, uint8_t current_bank) { uint8_t hi_ram = (mmu[0] & 0x30) >> 4; uint8_t mid_ram = (mmu[0] & 0x0c) >> 2; uint8_t lo_ram = (mmu[0] & 0x02) >> 1; uint8_t io_ram = (mmu[0] & 0x01); /* check if the needed bank is the current ram bank */ if (bank != current_bank) { /* needed bank is not current ram bank, so return not-valid */ return 0; } /* check if page is $00-$3f */ if (page <= 0x3f) { /* always ram, return valid */ return 1; } /* check if page is $40-$7f */ if (page >= 0x40 && page <= 0x7f) { if (lo_ram == 1) { return 1; } } /* check if page is $80-$bf */ if (page >= 0x80 && page <= 0xbf) { if (mid_ram == 3) { return 1; } } /* check if page is $c0-$cf or $e0-$fe */ if ((page >= 0xc0 && page <= 0xcf) || (page >= 0xe0 && page <= 0xfe)) { if (hi_ram == 3) { return 1; } } /* check if page is $d0-$df */ if (page >= 0xd0 && page <= 0xdf) { if (io_ram == 1 && hi_ram == 3) { return 1; } } /* rom area, so return not-valid */ return 0; } /* custom key handler, called when key either pressed or released */ static int mmu_4080_key_event(int pressed) { DBGKEY(("mmu_4080_key_event pressed:%d", pressed)); /*keyboard_custom_key_set(KBD_CUSTOM_4080, pressed); pressed = keyboard_custom_key_get(KBD_CUSTOM_4080);*/ if (pressed != 1) { pressed = 0; } /* caution, the resource value is 1 when the key is not pressed (enabled = 0) */ mmu_column4080_key = pressed ? 0 : 1; mem_pla_config_changed(); DBGKEY(("mmu_4080_key_event mmu_column4080_key:%d 40/80 column key: %s.", mmu_column4080_key, mmu_column4080_key ? "40cols" : "80cols")); return pressed; } static void mmu_switch_cpu(int value) { if (value) { #ifdef MMU_DEBUG log_message(mmu_log, "Switching to 8502 CPU."); #endif monitor_cpu_type_set_value(CPU_6502); z80_trigger_dma(); } else { #ifdef MMU_DEBUG log_message(mmu_log, "Switching to Z80 CPU."); #endif monitor_cpu_type_set_value(CPU_Z80); interrupt_trigger_dma(maincpu_int_status, maincpu_clk); } } void mmu_set_ram_bank(uint8_t value) { if (c128_full_banks) { ram_bank = mem_ram + (((long)value & 0xc0) << 10); } else { ram_bank = mem_ram + (((long)value & 0x40) << 10); } #ifdef MMU_DEBUG if (c128_full_banks) { log_message(mmu_log, "Set RAM bank %i.", (value & 0xc0) >> 6); } else { log_message(mmu_log, "Set RAM bank %i.", (value & 0x40) >> 6); } #endif } static void mmu_set_dma_bank(uint8_t value) { if (c128_full_banks) { dma_bank = mem_ram + (((long)value & 0xc0) << 10); } else { dma_bank = mem_ram + (((long)value & 0x40) << 10); } } static void mmu_update_page01_pointers(void) { /* update pointers for page 0/1 in case they or the shared RAM settings changed */ /* (shared window has priority over P0H/P1H) */ uint8_t page_zero_bank, page_one_bank, current_bank; if (c128_full_banks) { page_zero_bank = (mmu[0x8] & 0x3); page_one_bank = (mmu[0xa] & 0x3); current_bank = ((mmu[0] >> 6) & 0x3); } else { page_zero_bank = (mmu[0x8] & 0x1); page_one_bank = (mmu[0xa] & 0x1); current_bank = ((mmu[0] >> 6) & 0x1); } c128_mem_set_mmu_page_0_target_ram(mmu_is_valid_ram(mmu[0x7], page_zero_bank, current_bank)); c128_mem_set_mmu_page_1_target_ram(mmu_is_valid_ram(mmu[0x9], page_one_bank, current_bank)); c128_mem_set_mmu_page_0_bank(page_zero_bank); c128_mem_set_mmu_page_1_bank(page_one_bank); c128_mem_set_mmu_zp_sp_shared(mmu_is_in_shared_ram(0)); if (mmu_is_in_shared_ram(mmu[0x7] << 8)) { page_zero_bank = 0; } if (mmu_is_in_shared_ram(mmu[0x9] << 8)) { page_one_bank = 0; } mem_page_zero = mem_ram + (page_zero_bank << 16) + (mmu[0x7] << 8); mem_page_one = mem_ram + (page_one_bank << 16) + (mmu[0x9] << 8); } /* returns 1 if MMU is in C64 mode */ int mmu_is_c64config(void) { return (mmu[5] & 0x40) ? 1 : 0; } int in_c64_mode = -1; int c64_mode_bank = 0; static void mmu_switch_to_c64mode(void) { #ifdef MMU_DEBUG log_message(mmu_log, "mmu_switch_to_c64mode\n"); #endif #if 0 if (force_c64_mode) { #ifdef MMU_DEBUG log_message(mmu_log, "mmu_switch_to_c64mode: force_c64_mode\n"); #endif mmu_config64 = 0x07; /* force c64-compatible register values */ /* Note: Don't use mmu_store here, it calls this function. */ mmu[0] = 0x3e; mmu[5] = 0xf7; /* force standard addresses for stack and zeropage */ mmu[7] = 0; c128_mem_set_mmu_page_0(0); mmu_set_dma_bank(mmu[6]); mmu[8] = 0; mmu[9] = 1; c128_mem_set_mmu_page_1(1); mmu[10] = 0; mmu_update_page01_pointers(); } #endif machine_tape_init_c64(); if (in_c64_mode != 1) { mem_initialize_go64_memory_bank(mmu[6]); if (c128_full_banks) { c64_mode_bank = ((mmu[0] >> 6) & 0x3); } else { c64_mode_bank = ((mmu[0] >> 6) & 0x1); } in_c64_mode = 1; z80mem_update_config(8 + (mmu_config64 & 7)); } /* make sure mem_initialize_go64_memory_bank() is run first */ mem_update_config(mmu_config64); keyboard_alternative_set(1); machine_kbdbuf_reset_c64(); machine_autostart_reset_c64(); #if 0 force_c64_mode = 0; #endif } static void mmu_switch_to_c128mode(void) { #ifdef MMU_DEBUG log_message(mmu_log, "mmu_switch_to_c128mode\n"); #endif machine_tape_init_c128(); mem_update_config(NUM_CONFIGS64 + mmu[0]); z80mem_update_config((((mmu[0] & 0x1)) ? 0 : 1) | ((mmu[0] & 0x40) ? 2 : 0) | ((mmu[0] & 0x80) ? 4 : 0)); if (in_c64_mode != 0) { mem_initialize_go64_memory_bank(mmu[6]); in_c64_mode = 0; } keyboard_alternative_set(0); machine_kbdbuf_reset_c128(); machine_autostart_reset_c128(); } /* FIXME: for some reason it is not enough to call the sub functions only when the mode actually changes, as a result various init functions are now excessively called for mostly no reason */ static void mmu_update_config(void) { #ifdef MMU_DEBUG log_message(mmu_log, "MMU5 %02x, MMU0 %02x, MMUC %02x\n", mmu[5] & 0x40, mmu[0], mmu_config64); #endif if (mmu[5] & 0x40) { mmu_switch_to_c64mode(); } else { mmu_switch_to_c128mode(); } } void mmu_set_config64(int config) { mmu_config64 = config; mmu_update_config(); } /* ------------------------------------------------------------------------- */ uint8_t mmu_peek(uint16_t addr) { addr &= 0xff; #ifdef MMU_DEBUG log_message(mmu_log, "MMU READ $%x.", addr); #endif if (addr < 0xc) { switch (addr) { case 5: { uint8_t exrom = export.exrom; if (force_c64_mode) { exrom = 1; } /* 0x80 = 40/80 key released. */ return (mmu[5] & 0x0f) | (mmu_column4080_key ? 0x80 : 0) | ((export.game ^ 1) << 4) | ((exrom ^ 1) << 5); } case 8: case 10: /* P0H/P1H upper four bits are unused and always return 1 */ return mmu[addr] | 0xf0; case 11: /* always return 0x20 unless someone confirms it would ever return 0x40 in any of the bank2+3 expansions */ /* return ((c128_full_banks) ? 0x40 : 0x20); */ return 0x20; default: return mmu[addr]; } } else { return 0xff; } } uint8_t mmu_read(uint16_t addr) { vicii_handle_pending_alarms_external(0); return mmu_peek(addr); } void mmu_store(uint16_t address, uint8_t value) { vicii_handle_pending_alarms_external_write(); address &= 0xff; #ifdef MMU_DEBUG log_message(mmu_log, "MMU STORE $%x <- #$%x.", address, value); #endif if (address < 0xb) { uint8_t oldvalue; oldvalue = mmu[address]; mmu[address] = value; switch (address) { case 0: /* Configuration register (CR). */ mmu_set_ram_bank(value); #ifdef MMU_DEBUG log_message(mmu_log, "IO: %s BASLO: %s BASHI: %s KERNAL %s FUNCLO %s.", !(value & 0x1) ? "on" : "off", !(value & 0x2) ? "on" : "off", !(value & 0xc) ? "on" : "off", !(value & 0x30) ? "on" : "off", ((value & 0xc) == 0x4) ? "on" : "off"); #endif break; case 5: /* Mode configuration register (MCR). */ value = (value & 0x7f) | 0x30; if ((value & 1) ^ (oldvalue & 1)) { mmu_switch_cpu(value & 1); } if (((value & 0x40) ^ (oldvalue & 0x40)) && (value & 0x40)) { /* tell carts we are in c64 mode */ /* can't do this in mmu_switch_to_c64mode as cart_config calls it */ c128cartridge_switch_mode(1); /* turn off the forced exrom signal in the mmu_read */ force_c64_mode = 0; } c128fastiec_fast_cpu_direction(value & 8); break; case 6: /* RAM configuration register (RCR). */ mmu_set_dma_bank(value); /* must call mmu_set_dma_bank before mem_set_ram_config */ mem_set_ram_config(value); break; case 8: /* do not commit yet, update happens on write to p0l */ mmu[address] = oldvalue; p0h_latch = value; break; case 10: /* do not commit yet, update happens on write to p1l */ mmu[address] = oldvalue; p1h_latch = value; break; case 7: mmu[8] = p0h_latch; c128_mem_set_mmu_page_0(mmu[7]); #ifdef MMU_DEBUG log_message(mmu_log, "PAGE ZERO %05x PAGE ONE %05x", (mmu[0x8] & 0x1 ? 0x10000 : 0x00000) + (mmu[0x7] << 8), (mmu[0xa] & 0x1 ? 0x10000 : 0x00000) + (mmu[0x9] << 8)); #endif break; case 9: mmu[10] = p1h_latch; c128_mem_set_mmu_page_1(mmu[9]); #ifdef MMU_DEBUG log_message(mmu_log, "PAGE ZERO %05x PAGE ONE %05x", (mmu[0x8] & 0x1 ? 0x10000 : 0x00000) + (mmu[0x7] << 8), (mmu[0xa] & 0x1 ? 0x10000 : 0x00000) + (mmu[0x9] << 8)); #endif break; } mmu_update_page01_pointers(); mmu_update_config(); } } /* z80 version of the mmu read using in/out, the mmu i/o range for the z80 depends on the mmu i/o bit */ uint8_t z80_c128_mmu_read(uint16_t addr) { if (mmu[0] & 1) { return 0; } return mmu_read(addr); } /* z80 version of the mmu store using in/out, the mmu i/o range for the z80 depends on the mmu i/o bit */ void z80_c128_mmu_store(uint16_t address, uint8_t value) { if (!(mmu[0] & 1)) { mmu_store(address, value); } } /* $FF00 - $FFFF: RAM, Kernal or internal function ROM, with MMU at $FF00 - $FF04. */ uint8_t mmu_ffxx_read(uint16_t addr) { if (addr >= 0xff00 && addr <= 0xff04) { vicii.last_cpu_val = mmu[addr & 0xf]; } else if ((mmu[0] & 0x30) == 0x00) { vicii.last_cpu_val = hi_read(addr); } else if ((mmu[0] & 0x30) == 0x10) { vicii.last_cpu_val = internal_function_rom_read(addr); } else if ((mmu[0] & 0x30) == 0x20) { vicii.last_cpu_val = external_function_rom_read(addr); } else { vicii.last_cpu_val = top_shared_read(addr); } return vicii.last_cpu_val; } void mmu_ffxx_store(uint16_t addr, uint8_t value) { vicii.last_cpu_val = value; if (addr == 0xff00) { mmu_store(0, value); /* FIXME? [SRT] does reu_dma(-1) work here, or should it be deferred until later? */ reu_dma(-1); } else { if (addr <= 0xff04) { mmu_store(0, mmu[addr & 0xf]); } else { hi_store(addr, value); } } } int mmu_dump(void *context, uint16_t addr) { mon_out(" Bank $4000-$7FFF $8000-$BFFF $C000-$CFFF $D000-$DFFF $E000-$FFFF\n"); mon_out("CR: %d %12s %12s %12s %12s %12s\n", (mmu[0] & 0xc0) >> 6, (mmu[0] & 2) ? "RAM" : "BASIC ROM", (mmu[0] & 8) ? ((mmu[0] & 4) ? "RAM" : "Ext.Func.ROM") : ((mmu[0] & 4) ? "Int.Func.ROM" : "BASIC ROM"), (mmu[0] & 0x20) ? ((mmu[0] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[0] & 0x10) ? "Int.Func.ROM" : "Kernal ROM"), (mmu[0] & 1) ? ((mmu[0] & 0x20) ? ((mmu[0] & 0x10) ? "RAM" : "Ext. Func. ROM") : ((mmu[0] & 0x10) ? "Int. Func. ROM" : "Kernal ROM")) : "I/O", (mmu[0] & 0x20) ? ((mmu[0] & 0x10) ? "RAM" : "Ext Func.ROM") : ((mmu[0] & 0x10) ? "Int.Func.ROM" : "Kernal ROM")); mon_out("PCRA: %d %12s %12s %12s %12s %12s\n", (mmu[1] & 0xc0) >> 6, (mmu[1] & 2) ? "RAM" : "BASIC ROM", (mmu[1] & 8) ? ((mmu[1] & 4) ? "RAM" : "Ext.Func.ROM") : ((mmu[1] & 4) ? "Int.Func.ROM" : "BASIC ROM"), (mmu[1] & 0x20) ? ((mmu[1] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[1] & 0x10) ? "Int.Func.ROM" : "Kernal ROM"), (mmu[1] & 1) ? ((mmu[1] & 0x20) ? ((mmu[1] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[1] & 0x10) ? "Int.Func.ROM" : "Kernal ROM")) : "I/O", (mmu[1] & 0x20) ? ((mmu[1] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[1] & 0x10) ? "Int.Func.ROM" : "Kernal ROM")); mon_out("PCRB: %d %12s %12s %12s %12s %12s\n", (mmu[2] & 0xc0) >> 6, (mmu[2] & 2) ? "RAM" : "BASIC ROM", (mmu[2] & 8) ? ((mmu[2] & 4) ? "RAM" : "Ext.Func.ROM") : ((mmu[2] & 4) ? "Int.Func.ROM" : "BASIC ROM"), (mmu[2] & 0x20) ? ((mmu[2] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[2] & 0x10) ? "Int.Func.ROM" : "Kernal ROM"), (mmu[2] & 1) ? ((mmu[2] & 0x20) ? ((mmu[2] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[2] & 0x10) ? "Int.Func.ROM" : "Kernal ROM")) : "I/O", (mmu[2] & 0x20) ? ((mmu[2] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[2] & 0x10) ? "Int.Func.ROM" : "Kernal ROM")); mon_out("PCRC: %d %12s %12s %12s %12s %12s\n", (mmu[3] & 0xc0) >> 6, (mmu[3] & 2) ? "RAM" : "BASIC ROM", (mmu[3] & 8) ? ((mmu[3] & 4) ? "RAM" : "Ext.Func.ROM") : ((mmu[3] & 4) ? "Int.Func.ROM" : "BASIC ROM"), (mmu[3] & 0x20) ? ((mmu[3] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[3] & 0x10) ? "Int.Func.ROM" : "Kernal ROM"), (mmu[3] & 1) ? ((mmu[3] & 0x20) ? ((mmu[3] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[3] & 0x10) ? "Int.Func.ROM" : "Kernal ROM")) : "I/O", (mmu[3] & 0x20) ? ((mmu[3] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[3] & 0x10) ? "Int.Func.ROM" : "Kernal ROM")); mon_out("PCRD: %d %12s %12s %12s %12s %12s\n", (mmu[4] & 0xc0) >> 6, (mmu[4] & 2) ? "RAM" : "BASIC ROM", (mmu[4] & 8) ? ((mmu[4] & 4) ? "RAM" : "Ext.Func.ROM") : ((mmu[4] & 4) ? "Int.Func.ROM" : "BASIC ROM"), (mmu[4] & 0x20) ? ((mmu[1] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[4] & 0x10) ? "Int.Func.ROM" : "Kernal ROM"), (mmu[4] & 1) ? ((mmu[4] & 0x20) ? ((mmu[4] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[4] & 0x10) ? "Int.Func.ROM" : "Kernal ROM")) : "I/O", (mmu[4] & 0x20) ? ((mmu[4] & 0x10) ? "RAM" : "Ext.Func.ROM") : ((mmu[4] & 0x10) ? "Int.Func.ROM" : "Kernal ROM")); mon_out("MCR: 40/80 key: %s, Operating mode: %s, EXROM line: %d, GAME line: %d\n Fast serial: %s, Current CPU: %s\n", (mmu[5] & 0x80) ? "up" : "down", (mmu[5] & 0x40) ? "C64 mode" : "C128 mode", (mmu[5] & 0x20) >> 5, (mmu[5] & 0x10) >> 4, (mmu[5] & 8) ? "serial out" : "serial in", (mmu[5] & 1) ? "8502" : "Z80"); mon_out("RCR: VIC-II RAM bank: %d, Shared RAM location & size: %s, %s\n", (mmu[6] & 0xc0) >> 6, (mmu[6] & 8) ? ((mmu[6] & 4) ? "bottom and top" : "top") : ((mmu[6] & 4) ? "bottom" : "none"), (mmu[6] & 2) ? ((mmu[6] & 1) ? "16KiB" : "8KiB") : ((mmu[6] & 1) ? "4KiB" : "1KiB")); mon_out("Page 0 pointer: $%05X\n", (unsigned int)(((mmu[8] & 1) << 16) | (mmu[7] << 8))); mon_out("Page 1 pointer: $%05X\n", (unsigned int)(((mmu[10] & 1) << 16) | (mmu[9] << 8))); mon_out("MMU version: %d\n", mmu[11] & 0xf); mon_out("Amount of 64KiB blocks present: %d\n", (c128_full_banks) ? 4 : 2); return 0; } /* ------------------------------------------------------------------------- */ void mmu_init(void) { mmu_log = log_open("MMU"); DBGKEY(("mmu_init mmu_column4080_key:%d", mmu_column4080_key)); set_column4080_key(mmu_column4080_key, NULL); mmu[5] = 0; } void mmu_reset(void) { uint16_t i; for (i = 0; i < 0xb; i++) { mmu[i] = 0; } /* defaults */ mmu[7] = 0; c128_mem_set_mmu_page_0(mmu[7]); mmu[9] = 1; c128_mem_set_mmu_page_1(mmu[9]); mmu_update_page01_pointers(); mmu_set_dma_bank(mmu[6]); /* CAUTION: the registered function MUST NOT call keyboard_custom_key_set() */ keyboard_register_custom_key(KBD_CUSTOM_4080, mmu_4080_key_event, "40/80 column key", &key_ctrl_column4080, &key_flags_column4080); force_c64_mode = force_c64_mode_res; /* tell carts we are in c128 mode, or c64 if forced */ c128cartridge_switch_mode(force_c64_mode); }