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 <unistd.h>
21 
22 #include "kc/system.h"
23 
24 #include "kc/kc.h"
25 #include "kc/fdc.h"
26 #include "kc/disk.h"
27 #include "kc/floppy.h"
28 
29 #include "cmd/cmd.h"
30 
31 #include "sys/sysdep.h"
32 
33 #include "ui/status.h"
34 
35 #include "libdbg/dbg.h"
36 
37 class CMD_disk_attach : public CMD
38 {
39 private:
40   Disk *_d;
41   static const char * _path;
42 
43 protected:
get_disk_no(CMD_Args * args)44   int get_disk_no(CMD_Args *args)
45   {
46     int n = 0;
47 
48     if (args && args->has_arg("disk"))
49       n = args->get_long_arg("disk");
50 
51     return n;
52   }
53 
54 public:
CMD_disk_attach(Disk * d)55   CMD_disk_attach(Disk *d) : CMD("disk-attach")
56   {
57     _d = d;
58     register_cmd("disk-attach", 0);
59     register_cmd("disk-detach", 3);
60   }
61 
execute(CMD_Args * args,CMD_Context context)62   void execute(CMD_Args *args, CMD_Context context)
63     {
64       bool create;
65       char buf[1000];
66       disk_error_t err;
67       char *shortname;
68       const char *filename;
69 
70       create = false;
71       filename = NULL;
72       switch (context)
73         {
74           /*
75            *  disk-attach
76            */
77         case 0:
78           if (!args)
79             args = new CMD_Args();
80           filename = args->get_string_arg("filename");
81           if (!filename)
82             {
83               args->set_string_arg("ui-file-select-title",
84                                    _("Select disk..."));
85 	      if (_path)
86 		args->set_string_arg("ui-file-select-path", _path);
87               args->add_callback("ui-file-select-CB-ok", this, 1);
88               CMD_EXEC_ARGS("ui-file-select", args);
89               return;
90             }
91           break;
92           /*
93            *  ui-file-select-CB-ok
94            */
95         case 1:
96           if (args)
97             filename = args->get_string_arg("filename");
98           break;
99           /*
100            *  ui-dialog-yes-no-CB-yes
101            */
102         case 2:
103           if (args)
104             filename = args->get_string_arg("filename");
105           create = true;
106           break;
107           /*
108            *  disk-detach
109            */
110         case 3:
111           _d->detach(get_disk_no(args));
112           return;
113         }
114 
115       if (filename)
116         {
117 	  _path = filename;
118           err = _d->attach(get_disk_no(args), filename, create);
119           switch (err)
120             {
121             case DISK_NOENT:
122               if (!create)
123                 {
124                   args->set_string_arg("ui-dialog-title", _("create file?"));
125                   args->set_string_arg("ui-dialog-text",
126                                        _("The file '%s' doesn't exist.\n"
127                                          "Do you want to create it?"));
128                   args->set_string_arg("ui-dialog-text-arg", "filename");
129                   args->add_callback("ui-dialog-yes-no-CB-yes", this, 2);
130                   CMD_EXEC_ARGS("ui-dialog-yes-no", args);
131                 }
132               break;
133             case DISK_OK:
134               shortname = sys_basename(filename);
135               snprintf(buf, sizeof(buf), _("disk-file `%s' attached."), shortname);
136 	      free(shortname);
137               Status::instance()->setMessage(buf);
138               break;
139             default:
140               Status::instance()->setMessage(_("Can't attach disk-file."));
141               break;
142             }
143         }
144     }
145 };
146 
147 const char * CMD_disk_attach::_path = NULL;
148 
Disk()149 Disk::Disk()
150 {
151   _cmd = new CMD_disk_attach(this);
152 }
153 
~Disk()154 Disk::~Disk()
155 {
156   delete _cmd;
157 }
158 
159 bool
create_disk_file(FILE * f)160 Disk::create_disk_file(FILE *f)
161 {
162   for (int c = 0;c < 80;c++)
163     for (int h = 0;h < 2;h++)
164       for (int s = 1;s < 6;s++)
165 	if (!write_sector(f, c, h, s))
166 	  return false;
167 
168   return true;
169 }
170 
171 bool
write_sector(FILE * f,int c,int h,int s)172 Disk::write_sector(FILE *f, int c, int h, int s)
173 {
174   if (fputc(c, f) == EOF) // acyl
175     return false;
176   if (fputc(h, f) == EOF) // asid
177     return false;
178   if (fputc(c, f) == EOF) // lcyl
179     return false;
180   if (fputc(h, f) == EOF) // lsid
181     return false;
182   if (fputc(s, f) == EOF) // lsec
183     return false;
184   if (fputc(3, f) == EOF) // llen
185     return false;
186   if (fputc(0, f) == EOF) // count low
187     return false;
188   if (fputc(4, f) == EOF) // count high
189     return false;
190 
191   for (int a = 0;a < 1024;a++)
192     if (fputc(0xe5, f) == EOF)
193       return false;
194 
195   return true;
196 }
197 
198 disk_error_t
attach(int disk_no,const char * filename,bool create)199 Disk::attach(int disk_no, const char *filename, bool create)
200 {
201   char *ptr;
202   disk_error_t ret;
203 
204   if (fdc_fdc == NULL)
205     return DISK_ERROR;
206 
207   if (filename == NULL)
208     return DISK_ERROR;
209 
210   ret = DISK_OK;
211 
212   if (create)
213     {
214       DBG(1, form("KCemu/Disk/attach",
215 		  "Disk::attach(): [disk %d] create (%s)\n",
216 		  disk_no, filename));
217 
218       FILE *f = fopen(filename, "wb");
219       if (f == NULL)
220 	return DISK_ERROR;
221 
222       bool create_ok = create_disk_file(f);
223       fclose(f);
224 
225       if (!create_ok)
226 	return DISK_ERROR;
227     }
228 
229   DBG(1, form("KCemu/Disk/attach",
230 	      "Disk::attach(): [disk %d] open (%s)\n",
231 	      disk_no, filename));
232 
233   Floppy *floppy = fdc_fdc->get_floppy(disk_no);
234   if (floppy != NULL)
235     {
236       if (access(filename, R_OK) == 0)
237 	{
238 	  ptr = strdup(filename);
239 	}
240       else
241 	{
242 	  ptr = (char *)malloc(strlen(kcemu_datadir) + strlen(filename) + 11);
243 	  strcpy(ptr, kcemu_datadir);
244 	  strcat(ptr, "/disks/");
245 	  strcat(ptr, filename);
246 	  if (access(ptr, R_OK) != 0)
247 	    strcat(ptr, ".gz");
248 	}
249 
250       if (access(ptr, R_OK) == 0)
251 	{
252 	  if (!floppy->attach(ptr))
253 	    ret = DISK_ERROR;
254 	}
255       else
256 	{
257 	  ret = DISK_NOENT;
258 	}
259 
260       free(ptr);
261     }
262 
263   CMD_Args *args = new CMD_Args();
264   args->set_long_arg("disk", disk_no);
265   args->set_string_arg("filename", filename);
266   CMD_EXEC_ARGS("ui-disk-update-MSG", args);
267   delete args;
268 
269   return ret;
270 }
271 
272 disk_error_t
detach(int disk_no)273 Disk::detach(int disk_no)
274 {
275   if (fdc_fdc == NULL)
276     return DISK_ERROR;
277 
278   DBG(1, form("KCemu/Disk/detach",
279 	      "Disk::detach(): [disk %d] close\n",
280 	      disk_no));
281 
282   Floppy *floppy = fdc_fdc->get_floppy(disk_no);
283   if (floppy != NULL)
284     floppy->attach(NULL);
285 
286   CMD_Args *args = new CMD_Args();
287   args->set_long_arg("disk", disk_no);
288   args->set_string_arg("filename", "");
289   CMD_EXEC_ARGS("ui-disk-update-MSG", args);
290   delete args;
291 
292   return DISK_OK;
293 }
294