OpenCBM
upload.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version
5  * 2 of the License, or (at your option) any later version.
6  *
7  * Copyright 1999-2005 Michael Klein <michael(dot)klein(at)puffin(dot)lb(dot)shuttle(dot)de>
8  * Copyright 2001-2005,2008 Spiro Trikaliotis
9  *
10 */
11 
21 #define DBG_USERMODE
22 
24 #define DBG_PROGNAME "OPENCBM.DLL"
25 
26 #include "debug.h"
27 
28 #include <stdlib.h>
29 
31 #define DLL
32 #include "opencbm.h"
33 #include "archlib.h"
34 
35 
36 /*-------------------------------------------------------------------*/
37 /*--------- HELPER FUNCTIONS ----------------------------------------*/
38 
51 static __inline void StoreInt8IntoBuffer(unsigned char * Buffer, unsigned int Value)
52 {
53  DBG_ASSERT(Buffer != NULL);
54  *Buffer = (unsigned char) Value;
55 }
56 
70 static __inline void StoreInt16IntoBuffer(unsigned char * Buffer, unsigned int Value)
71 {
72  DBG_ASSERT(Buffer != NULL);
73  StoreInt8IntoBuffer(Buffer++, Value % 256);
74  StoreInt8IntoBuffer(Buffer, Value / 256);
75 }
76 
94 static __inline void StoreAddressAndCount(unsigned char * Buffer, unsigned int DriveMemAddress, unsigned int ByteCount)
95 {
96  StoreInt16IntoBuffer(Buffer, DriveMemAddress);
97  StoreInt8IntoBuffer(Buffer + 2, ByteCount);
98 }
99 
132 int CBMAPIDECL
133 cbm_upload(CBM_FILE HandleDevice, unsigned char DeviceAddress,
134  int DriveMemAddress, const void *Program, size_t Size)
135 {
136  const char *bufferToProgram = Program;
137 
138  unsigned char command[] = { 'M', '-', 'W', ' ', ' ', ' ' };
139  size_t i;
140  int rv = 0;
141  int c;
142 
143  FUNC_ENTER();
144 
145  DBG_ASSERT(sizeof(command) == 6);
146 
147  for(i = 0; i < Size; i += 32)
148  {
149  if ( cbm_listen(HandleDevice, DeviceAddress, 15) ) {
150  return -1;
151  }
152 
153  // Calculate how many bytes are left
154 
155  c = Size - i;
156 
157  // Do we have more than 32? Then, restrict to 32
158 
159  if (c > 32)
160  {
161  c = 32;
162  }
163 
164  // The command M-W consists of:
165  // M-W <lowaddress> <highaddress> <count>
166  // build that command:
167 
168  StoreAddressAndCount(&command[3], DriveMemAddress, c);
169 
170  // Write the M-W command to the drive...
171 
172  if ( cbm_raw_write(HandleDevice, command, sizeof(command)) != sizeof command) {
173  rv = -1;
174  break;
175  }
176 
177 
178  // ... as well as the (up to 32) data bytes
179 
180  if ( cbm_raw_write(HandleDevice, bufferToProgram, c) != c ) {
181  rv = -1;
182  break;
183  }
184 
185  // Now, advance the pointer into drive memory
186  // as well to the program in PC's memory in case we
187  // might need to use it again for another M-W command
188 
189  DriveMemAddress += c;
190  bufferToProgram += c;
191 
192  // Advance the return value of send bytes, too.
193 
194  rv += c;
195 
196  // The UNLISTEN is the signal for the drive
197  // to start execution of the command
198 
199  if ( cbm_unlisten(HandleDevice) ) {
200  rv = -1;
201  break;
202  }
203  }
204 
205  FUNC_LEAVE_INT(rv);
206 }
207 
240 enum { TRANSFER_SIZE_DOWNLOAD = 0x100u };
241 
242 int CBMAPIDECL
243 cbm_download(CBM_FILE HandleDevice, unsigned char DeviceAddress,
244  int DriveMemAddress, void *const Buffer, size_t Size)
245 {
246  unsigned char command[] = { 'M', '-', 'R', ' ', ' ', '\0', '\r' };
247  unsigned char *StoreBuffer = Buffer;
248 
249  size_t i;
250  int rv = 0;
251  int readbytes = 0;
252  int c;
253  int page2workaround = 0;
254 
255  FUNC_ENTER();
256 
257  DBG_ASSERT(sizeof(command) == 7);
258 
259  for(i = 0; i < Size; i += c)
260  {
261  char dummy;
262 
263  // Calculate how much bytes are left
264  c = Size - i;
265 
266  // Do we have more than 256? Then, restrict to 256
267  if (c > TRANSFER_SIZE_DOWNLOAD)
268  {
269  c = TRANSFER_SIZE_DOWNLOAD;
270  }
271 
272  /*
273  * Workaround: The 154x/157x/1581 drives (and possibly others, too)
274  * cannot cross a page boundary on M-R. Thus, make sure we do not
275  * cross the boundary.
276  */
277  if (c + (DriveMemAddress & 0xFF) > 0x100) {
278  c = 0x100 - (DriveMemAddress & 0xFF);
279  }
280 
281  // The command M-R consists of:
282  // M-R <lowaddress> <highaddress> <count> '\r'
283  // build that command:
284 
285  StoreAddressAndCount(&command[3], DriveMemAddress, c);
286 
287  // Write the M-R command to the drive...
288  if ( cbm_exec_command(HandleDevice, DeviceAddress, command, sizeof(command)) ) {
289  rv = -1;
290  break;
291  }
292 
293 
294  if ( cbm_talk(HandleDevice, DeviceAddress, 15) ) {
295  rv = -1;
296  break;
297  }
298 
299  // now read the (up to 256) data bytes
300  // and advance the return value of send bytes, too.
301  readbytes = cbm_raw_read(HandleDevice, StoreBuffer, c);
302 
303  // Now, advance the pointer into drive memory
304  // as well to the program in PC's memory in case we
305  // might need to use it again for another M-W command
306  DriveMemAddress += readbytes;
307  StoreBuffer += readbytes;
308 
309  rv += readbytes;
310 
311  // skip the trailing CR
312  if ( cbm_raw_read(HandleDevice, &dummy, 1) != 1 ) {
313  /*
314  * if there is no CR, there can be two reasons:
315  *
316  * 1. an unexpected error occurred. In this case, we
317  * want to quit and report an error
318  *
319  * 2. we are reading page 2 of the floppy drive's memory
320  * In this case, this error is due to the special
321  * handling of the $02D4 address in the 154x/157x
322  * ($02CF in the 1581). It returns a CR and aborts the
323  * transfer.
324  * If this is the case, restart the transmission for
325  * the rest as a work-around.
326  */
327 
328  if ( ( (DriveMemAddress & 0xFF00) >> 8 == 2) && page2workaround == 0 ) {
329  /* we are on page 2, try the workaround once */
330  page2workaround = 1;
331  c = readbytes - 1;
332  --rv;
333  --DriveMemAddress;
334  --StoreBuffer;
335  continue;
336  }
337  else {
338  rv = -1;
339  break;
340  }
341  }
342 
343  // The UNTALK is the signal for end of transmission
344  if ( cbm_untalk(HandleDevice) ) {
345  rv = -1;
346  break;
347  }
348  }
349 
350  FUNC_LEAVE_INT(rv);
351 }
int CBMAPIDECL cbm_talk(CBM_FILE HandleDevice, unsigned char DeviceAddress, unsigned char SecondaryAddress)
Send a TALK on the IEC serial bus.
Definition: cbm.c:976
#define FUNC_LEAVE_INT(_xxx)
Definition: debug.h:358
int CBMAPIDECL cbm_listen(CBM_FILE HandleDevice, unsigned char DeviceAddress, unsigned char SecondaryAddress)
Send a LISTEN on the IEC serial bus.
Definition: cbm.c:945
Define makros for debugging purposes.
#define CBMAPIDECL
Definition: opencbm.h:85
int CBMAPIDECL cbm_raw_write(CBM_FILE HandleDevice, const void *Buffer, size_t Count)
Write data to the IEC serial bus.
Definition: cbm.c:870
#define CBM_FILE
Definition: opencbm.h:87
int CBMAPIDECL cbm_unlisten(CBM_FILE HandleDevice)
Send an UNLISTEN on the IEC serial bus.
Definition: cbm.c:1100
#define DBG_ASSERT(_xxx)
Definition: debug.h:401
int CBMAPIDECL cbm_raw_read(CBM_FILE HandleDevice, void *Buffer, size_t Count)
Read data from the IEC serial bus.
Definition: cbm.c:906
int CBMAPIDECL cbm_exec_command(CBM_FILE HandleDevice, unsigned char DeviceAddress, const void *Command, size_t Size)
Executes a command in the floppy drive.
Definition: cbm.c:1599
#define FUNC_ENTER()
Definition: debug.h:347
DLL interface for accessing the driver.
int CBMAPIDECL cbm_untalk(CBM_FILE HandleDevice)
Send an UNTALK on the IEC serial bus.
Definition: cbm.c:1128
int CBMAPIDECL cbm_upload(CBM_FILE HandleDevice, unsigned char DeviceAddress, int DriveMemAddress, const void *Program, size_t Size)
Upload a program into a floppy's drive memory.
Definition: upload.c:133