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