1 /*
2 * KCemu -- The emulator for the KC85 homecomputer series and much more.
3 * Copyright (C) 1997-2010 Torsten Paul
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <stdlib.h>
21 #include <fstream>
22
23 #include "kc/system.h"
24 #include "kc/prefs/prefs.h"
25
26 #include "kc/z80.h"
27 #include "kc/memory7.h"
28
29 #include "ui/ui.h"
30
31 #include "libdbg/dbg.h"
32
33 using namespace std;
34
Memory7(void)35 Memory7::Memory7(void) : Memory()
36 {
37 load_rom(SystemROM::ROM_KEY_SYSTEM, &_rom_os);
38 load_rom(SystemROM::ROM_KEY_BASIC, &_rom_basic);
39 load_rom(SystemROM::ROM_KEY_CHARGEN, &_rom_chargen);
40
41 memset(&_irm[0], 0x70, 0x400);
42
43 memory_group_t mem[] = {
44 { &_m_scr, "-", 0x0000, 0x10000, 0, 256, 0, 1, -1 },
45 { &_m_ram, "RAM", 0x0000, 0x4000, &_ram[0], 0, 0, 1, -1 },
46 { &_m_basic, "BASIC", 0xc000, 0x2800, &_rom_basic[0], 0, 1, 1, -1 },
47 { &_m_os, "OS", 0xf000, 0x1000, &_rom_os[0], 0, 1, 1, -1 },
48 { &_m_irm_ec,"IRM (text)", 0xec00, 0x0400, &_irm[0x400], 1, 0, 1, -1 },
49 /*
50 * dummy entry needed for get_irm() if color
51 * expansion is not installed
52 */
53 { &_m_irm_e8,"IRM (color)", 0xe800, 0x0400, &_irm[0], 1, 1, 1, -1 },
54 { 0, },
55 };
56 init_memory_groups(mem);
57
58 reset(true);
59 z80->register_ic(this);
60
61 register_romdi_handler(this);
62 set_romdi(false);
63 }
64
~Memory7(void)65 Memory7::~Memory7(void)
66 {
67 z80->unregister_ic(this);
68 unregister_romdi_handler(this);
69
70 delete _m_scr;
71 delete _m_ram;
72 delete _m_basic;
73 delete _m_os;
74 delete _m_irm_e8;
75 delete _m_irm_ec;
76 }
77
78 byte_t
memRead8(word_t addr)79 Memory7::memRead8(word_t addr)
80 {
81 for (memory_list_t::iterator it = _memory_list.begin();it != _memory_list.end();it++)
82 (*it)->memory_read_byte(addr);
83
84 return _memrptr[addr >> MemArea::PAGE_SHIFT][addr & MemArea::PAGE_MASK];
85 }
86
87 void
memWrite8(word_t addr,byte_t val)88 Memory7::memWrite8(word_t addr, byte_t val)
89 {
90 for (memory_list_t::iterator it = _memory_list.begin();it != _memory_list.end();it++)
91 (*it)->memory_write_byte(addr, val);
92
93 _memwptr[addr >> MemArea::PAGE_SHIFT][addr & MemArea::PAGE_MASK] = val;
94 }
95
96 byte_t *
get_irm(void)97 Memory7::get_irm(void)
98 {
99 return (byte_t *)get_page_addr_r(0xe800);
100 }
101
102 byte_t *
get_char_rom(void)103 Memory7::get_char_rom(void)
104 {
105 return (byte_t *)_rom_chargen;
106 }
107
108 void
register_romdi_handler(ROMDIInterface * handler)109 Memory7::register_romdi_handler(ROMDIInterface *handler)
110 {
111 DBG(1, form("KCemu/Memory7/romdi",
112 "Memory7::register_romdi_handler(): %p\n",
113 handler));
114
115 _romdi_list.push_back(handler);
116 }
117
118 void
unregister_romdi_handler(ROMDIInterface * handler)119 Memory7::unregister_romdi_handler(ROMDIInterface *handler)
120 {
121 DBG(1, form("KCemu/Memory7/romdi",
122 "Memory7::unregister_romdi_handler(): %p\n",
123 handler));
124
125 _romdi_list.remove(handler);
126 }
127
128 void
set_romdi(bool val)129 Memory7::set_romdi(bool val)
130 {
131 DBG(1, form("KCemu/Memory7/romdi",
132 "Memory7::set_romdi(): %s\n",
133 val ? "on" : "off"));
134
135 _romdi = val;
136 for (romdi_list_t::iterator it = _romdi_list.begin();it != _romdi_list.end();it++)
137 (*it)->romdi(val);
138
139 reload_mem_ptr();
140 }
141
142 void
romdi(bool val)143 Memory7::romdi(bool val)
144 {
145 DBG(1, form("KCemu/Memory7/romdi",
146 "Memory7::romdi(): BASIC ROM %s\n",
147 val ? "off" : "on"));
148
149 _m_basic->set_active(!val);
150 }
151
152 void
register_memory_handler(MemoryInterface * handler)153 Memory7::register_memory_handler(MemoryInterface *handler)
154 {
155 _memory_list.push_back(handler);
156 }
157
158 void
unregister_memory_handler(MemoryInterface * handler)159 Memory7::unregister_memory_handler(MemoryInterface *handler)
160 {
161 _memory_list.remove(handler);
162 }
163
164 void
reset(bool power_on)165 Memory7::reset(bool power_on)
166 {
167 if (!power_on)
168 return;
169
170 scratch_mem(&_ram[0], 0x4000);
171 scratch_mem(&_irm[0x0400], 0x0400);
172 if (get_irm() != _irm)
173 scratch_mem(&_irm[0x0], 0x0400);
174 else
175 memset(&_irm[0], 0x70, 0x400);
176
177 /*
178 * Clear the first 1k of ram with the system variables. This saves
179 * some trouble with the initialization.
180 */
181 memset(&_ram[0], 0, 0x400);
182
183 /*
184 * The CPM-Z9 boot module is enabled/disabled by writing to address
185 * ranges f800h-fbffh/fc00h-ffffh. The delete cursor routine at
186 * fa33h uses the cursor address (2dh/2eh) which is initialized by
187 * using cursor row/column from 2bh/2ch (although after using the
188 * cursor address first).
189 *
190 * If the cursor address holds random values we may get memory
191 * writes at addresses that disable the boot module at power on.
192 *
193 * To prevent the following initialization to be overwritten in the
194 * startup routine we also need to initialize the EOR (end of ram)
195 * pointer.
196 *
197 * How is this supposed to work on the real machine?
198 */
199 _ram[0x2b] = 0x01; /* column */
200 _ram[0x2c] = 0x01; /* row */
201 _ram[0x2d] = 0x55; /* cursor address low */
202 _ram[0x2e] = 0x55; /* cursor address high */
203
204 _ram[0x36] = 0x00; /* logical ram end low */
205 _ram[0x37] = 0xc0; /* logical ram end high */
206 }
207
208 void
dumpCore(void)209 Memory7::dumpCore(void)
210 {
211 ofstream os;
212
213 os.open("core.z80");
214
215 cerr << "Memory: dumping core..." << endl;
216 if (!os)
217 {
218 cerr << "Memory: can't write 'core.z80'" << endl;
219 return;
220 }
221
222 for (int a = 0;a < 0x10000;a++)
223 os.put(memRead8(a));
224
225 os.close();
226 cerr << "Memory: done." << endl;
227 }
228