src/emu/uiimage.c
| r9927 | r9928 | |
| 23 | 23 | #include "uimenu.h" |
| 24 | 24 | #include "zippath.h" |
| 25 | 25 | #include "unicode.h" |
| 26 | #include "imagedev/cassette.h" |
| 27 | #include "imagedev/bitbngr.h" |
| 26 | 28 | |
| 27 | 29 | |
| 28 | 30 | |
| r9927 | r9928 | |
| 1014 | 1016 | /* process the menu */ |
| 1015 | 1017 | ui_menu_process(machine, menu, 0); |
| 1016 | 1018 | } |
| 1019 | |
| 1020 | /*************************************************************************** |
| 1021 | TYPE DEFINITIONS |
| 1022 | ***************************************************************************/ |
| 1023 | #define TAPECMD_NULL ((void *) 0x0000) |
| 1024 | #define TAPECMD_STOP ((void *) 0x0001) |
| 1025 | #define TAPECMD_PLAY ((void *) 0x0002) |
| 1026 | #define TAPECMD_RECORD ((void *) 0x0003) |
| 1027 | #define TAPECMD_REWIND ((void *) 0x0004) |
| 1028 | #define TAPECMD_FAST_FORWARD ((void *) 0x0005) |
| 1029 | #define TAPECMD_SLIDER ((void *) 0x0006) |
| 1030 | #define TAPECMD_SELECT ((void *) 0x0007) |
| 1031 | |
| 1032 | #define BITBANGERCMD_SELECT ((void *) 0x0000) |
| 1033 | #define BITBANGERCMD_MODE ((void *) 0x0001) |
| 1034 | #define BITBANGERCMD_BAUD ((void *) 0x0002) |
| 1035 | #define BITBANGERCMD_TUNE ((void *) 0x0003) |
| 1036 | |
| 1037 | |
| 1038 | typedef struct _tape_control_menu_state tape_control_menu_state; |
| 1039 | struct _tape_control_menu_state |
| 1040 | { |
| 1041 | int index; |
| 1042 | device_image_interface *device; |
| 1043 | }; |
| 1044 | |
| 1045 | |
| 1046 | |
| 1047 | typedef struct _bitbanger_control_menu_state bitbanger_control_menu_state; |
| 1048 | struct _bitbanger_control_menu_state |
| 1049 | { |
| 1050 | int index; |
| 1051 | device_image_interface *device; |
| 1052 | }; |
| 1053 | |
| 1054 | |
| 1055 | |
| 1056 | /*************************************************************************** |
| 1057 | IMPLEMENTATION |
| 1058 | ***************************************************************************/ |
| 1059 | |
| 1060 | /*------------------------------------------------- |
| 1061 | cassette_count - returns the number of cassette |
| 1062 | devices in the machine |
| 1063 | -------------------------------------------------*/ |
| 1064 | |
| 1065 | INLINE int cassette_count( running_machine *machine ) |
| 1066 | { |
| 1067 | int count = 0; |
| 1068 | device_t *device = machine->m_devicelist.first(CASSETTE); |
| 1069 | |
| 1070 | while ( device ) |
| 1071 | { |
| 1072 | count++; |
| 1073 | device = device->typenext(); |
| 1074 | } |
| 1075 | return count; |
| 1076 | } |
| 1077 | |
| 1078 | /*------------------------------------------------- |
| 1079 | bitbanger_count - returns the number of bitbanger |
| 1080 | devices in the machine |
| 1081 | -------------------------------------------------*/ |
| 1082 | |
| 1083 | INLINE int bitbanger_count( running_machine *machine ) |
| 1084 | { |
| 1085 | int count = 0; |
| 1086 | device_t *device = machine->m_devicelist.first(BITBANGER); |
| 1087 | |
| 1088 | while ( device ) |
| 1089 | { |
| 1090 | count++; |
| 1091 | device = device->typenext(); |
| 1092 | } |
| 1093 | return count; |
| 1094 | } |
| 1095 | |
| 1096 | /*------------------------------------------------- |
| 1097 | tapecontrol_gettime - returns a textual |
| 1098 | representation of the time |
| 1099 | -------------------------------------------------*/ |
| 1100 | |
| 1101 | astring *tapecontrol_gettime(astring *dest, device_t *device, int *curpos, int *endpos) |
| 1102 | { |
| 1103 | double t0, t1; |
| 1104 | |
| 1105 | t0 = cassette_get_position(device); |
| 1106 | t1 = cassette_get_length(device); |
| 1107 | |
| 1108 | if (t1) |
| 1109 | astring_printf(dest, "%04d/%04d", (int) t0, (int) t1); |
| 1110 | else |
| 1111 | astring_printf(dest, "%04d/%04d", 0, (int) t1); |
| 1112 | |
| 1113 | if (curpos != NULL) |
| 1114 | *curpos = t0; |
| 1115 | if (endpos != NULL) |
| 1116 | *endpos = t1; |
| 1117 | |
| 1118 | return dest; |
| 1119 | } |
| 1120 | |
| 1121 | |
| 1122 | |
| 1123 | /*------------------------------------------------- |
| 1124 | menu_tape_control_populate - populates the |
| 1125 | main tape control menu |
| 1126 | -------------------------------------------------*/ |
| 1127 | |
| 1128 | static void menu_tape_control_populate(running_machine *machine, ui_menu *menu, tape_control_menu_state *menustate) |
| 1129 | { |
| 1130 | astring timepos; |
| 1131 | cassette_state state; |
| 1132 | int count = cassette_count(machine); |
| 1133 | UINT32 flags = 0; |
| 1134 | |
| 1135 | if( count > 0 ) |
| 1136 | { |
| 1137 | if( menustate->index == (count-1) ) |
| 1138 | flags |= MENU_FLAG_LEFT_ARROW; |
| 1139 | else |
| 1140 | flags |= MENU_FLAG_RIGHT_ARROW; |
| 1141 | } |
| 1142 | |
| 1143 | if (menustate->device->exists()) |
| 1144 | { |
| 1145 | double t0, t1; |
| 1146 | UINT32 tapeflags = 0; |
| 1147 | |
| 1148 | t0 = cassette_get_position(&menustate->device->device()); |
| 1149 | t1 = cassette_get_length(&menustate->device->device()); |
| 1150 | |
| 1151 | if (t1 > 0) |
| 1152 | { |
| 1153 | if (t0 > 0) |
| 1154 | tapeflags |= MENU_FLAG_LEFT_ARROW; |
| 1155 | if (t0 < t1) |
| 1156 | tapeflags |= MENU_FLAG_RIGHT_ARROW; |
| 1157 | } |
| 1158 | |
| 1159 | /* name of tape */ |
| 1160 | ui_menu_item_append(menu, menustate->device->image_config().devconfig().name(), menustate->device->filename(), flags, TAPECMD_SELECT); |
| 1161 | |
| 1162 | /* state */ |
| 1163 | tapecontrol_gettime(&timepos, &menustate->device->device(), NULL, NULL); |
| 1164 | state = cassette_get_state(&menustate->device->device()); |
| 1165 | ui_menu_item_append( |
| 1166 | menu, |
| 1167 | (state & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED |
| 1168 | ? "stopped" |
| 1169 | : ((state & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY |
| 1170 | ? ((state & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED ? "playing" : "(playing)") |
| 1171 | : ((state & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED ? "recording" : "(recording)") |
| 1172 | ), |
| 1173 | astring_c(&timepos), |
| 1174 | tapeflags, |
| 1175 | TAPECMD_SLIDER); |
| 1176 | |
| 1177 | /* pause or stop */ |
| 1178 | ui_menu_item_append(menu, "Pause/Stop", NULL, 0, TAPECMD_STOP); |
| 1179 | |
| 1180 | /* play */ |
| 1181 | ui_menu_item_append(menu, "Play", NULL, 0, TAPECMD_PLAY); |
| 1182 | |
| 1183 | /* record */ |
| 1184 | ui_menu_item_append(menu, "Record", NULL, 0, TAPECMD_RECORD); |
| 1185 | |
| 1186 | /* rewind */ |
| 1187 | ui_menu_item_append(menu, "Rewind", NULL, 0, TAPECMD_REWIND); |
| 1188 | |
| 1189 | /* fast forward */ |
| 1190 | ui_menu_item_append(menu, "Fast Forward", NULL, 0, TAPECMD_FAST_FORWARD); |
| 1191 | } |
| 1192 | else |
| 1193 | { |
| 1194 | /* no tape loaded */ |
| 1195 | ui_menu_item_append(menu, "No Tape Image loaded", NULL, flags, NULL); |
| 1196 | } |
| 1197 | } |
| 1198 | |
| 1199 | |
| 1200 | /*------------------------------------------------- |
| 1201 | menu_bitbanger_control_populate - populates the |
| 1202 | main bitbanger control menu |
| 1203 | -------------------------------------------------*/ |
| 1204 | |
| 1205 | static void menu_bitbanger_control_populate(running_machine *machine, ui_menu *menu, bitbanger_control_menu_state *menustate) |
| 1206 | { |
| 1207 | int count = bitbanger_count(machine); |
| 1208 | UINT32 flags = 0, mode_flags = 0, baud_flags = 0, tune_flags = 0; |
| 1209 | |
| 1210 | if( count > 0 ) |
| 1211 | { |
| 1212 | if( menustate->index == (count-1) ) |
| 1213 | flags |= MENU_FLAG_LEFT_ARROW; |
| 1214 | else |
| 1215 | flags |= MENU_FLAG_RIGHT_ARROW; |
| 1216 | } |
| 1217 | |
| 1218 | if (bitbanger_inc_mode(&menustate->device->device(), TRUE)) |
| 1219 | mode_flags |= MENU_FLAG_RIGHT_ARROW; |
| 1220 | |
| 1221 | if (bitbanger_dec_mode(&menustate->device->device(), TRUE)) |
| 1222 | mode_flags |= MENU_FLAG_LEFT_ARROW; |
| 1223 | |
| 1224 | if (bitbanger_inc_baud(&menustate->device->device(), TRUE)) |
| 1225 | baud_flags |= MENU_FLAG_RIGHT_ARROW; |
| 1226 | |
| 1227 | if (bitbanger_dec_baud(&menustate->device->device(), TRUE)) |
| 1228 | baud_flags |= MENU_FLAG_LEFT_ARROW; |
| 1229 | |
| 1230 | if (bitbanger_inc_tune(&menustate->device->device(), TRUE)) |
| 1231 | tune_flags |= MENU_FLAG_RIGHT_ARROW; |
| 1232 | |
| 1233 | if (bitbanger_dec_tune(&menustate->device->device(), TRUE)) |
| 1234 | tune_flags |= MENU_FLAG_LEFT_ARROW; |
| 1235 | |
| 1236 | |
| 1237 | if (menustate->device->exists()) |
| 1238 | { |
| 1239 | /* name of bitbanger file */ |
| 1240 | ui_menu_item_append(menu, menustate->device->image_config().devconfig().name(), menustate->device->filename(), flags, BITBANGERCMD_SELECT); |
| 1241 | ui_menu_item_append(menu, "Device Mode:", bitbanger_mode_string(&menustate->device->device()), mode_flags, BITBANGERCMD_MODE); |
| 1242 | ui_menu_item_append(menu, "Baud:", bitbanger_baud_string(&menustate->device->device()), baud_flags, BITBANGERCMD_BAUD); |
| 1243 | ui_menu_item_append(menu, "Baud Tune:", bitbanger_tune_string(&menustate->device->device()), tune_flags, BITBANGERCMD_TUNE); |
| 1244 | ui_menu_item_append(menu, "Protocol:", "8-1-N", 0, NULL); |
| 1245 | } |
| 1246 | else |
| 1247 | { |
| 1248 | /* no tape loaded */ |
| 1249 | ui_menu_item_append(menu, "No Bitbanger Image loaded", NULL, flags, NULL); |
| 1250 | } |
| 1251 | } |
| 1252 | |
| 1253 | |
| 1254 | /*------------------------------------------------- |
| 1255 | menu_tape_control - main tape control menu |
| 1256 | -------------------------------------------------*/ |
| 1257 | |
| 1258 | void ui_mess_menu_tape_control(running_machine *machine, ui_menu *menu, void *parameter, void *state) |
| 1259 | { |
| 1260 | tape_control_menu_state *menustate; |
| 1261 | const ui_menu_event *event; |
| 1262 | |
| 1263 | /* if no state, allocate some */ |
| 1264 | if (state == NULL) |
| 1265 | state = ui_menu_alloc_state(menu, sizeof(*menustate), NULL); |
| 1266 | menustate = (tape_control_menu_state *) state; |
| 1267 | |
| 1268 | /* do we have to load the device? */ |
| 1269 | if (menustate->device == NULL) |
| 1270 | { |
| 1271 | int index = menustate->index; |
| 1272 | device_image_interface *device = NULL; |
| 1273 | for (bool gotone = machine->m_devicelist.first(device); gotone; gotone = device->next(device)) |
| 1274 | { |
| 1275 | if(device->device().type() == CASSETTE) { |
| 1276 | if (index==0) break; |
| 1277 | index--; |
| 1278 | } |
| 1279 | } |
| 1280 | menustate->device = device; |
| 1281 | ui_menu_reset(menu, (ui_menu_reset_options)0); |
| 1282 | } |
| 1283 | |
| 1284 | /* rebuild the menu - we have to do this so that the counter updates */ |
| 1285 | ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION); |
| 1286 | menu_tape_control_populate(machine, menu, (tape_control_menu_state*)state); |
| 1287 | |
| 1288 | /* process the menu */ |
| 1289 | event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT); |
| 1290 | if (event != NULL) |
| 1291 | { |
| 1292 | switch(event->iptkey) |
| 1293 | { |
| 1294 | case IPT_UI_LEFT: |
| 1295 | if (event->itemref==TAPECMD_SLIDER) |
| 1296 | cassette_seek(&menustate->device->device(), -1, SEEK_CUR); |
| 1297 | else |
| 1298 | if (event->itemref==TAPECMD_SELECT) |
| 1299 | { |
| 1300 | /* left arrow - rotate left through cassette devices */ |
| 1301 | if (menustate->index > 0) |
| 1302 | menustate->index--; |
| 1303 | else |
| 1304 | menustate->index = cassette_count(machine) - 1; |
| 1305 | menustate->device = NULL; |
| 1306 | } |
| 1307 | break; |
| 1308 | |
| 1309 | case IPT_UI_RIGHT: |
| 1310 | if (event->itemref==TAPECMD_SLIDER) |
| 1311 | cassette_seek(&menustate->device->device(), +1, SEEK_CUR); |
| 1312 | else |
| 1313 | if (event->itemref==TAPECMD_SELECT) |
| 1314 | { |
| 1315 | /* right arrow - rotate right through cassette devices */ |
| 1316 | if (menustate->index < cassette_count(machine) - 1) |
| 1317 | menustate->index++; |
| 1318 | else |
| 1319 | menustate->index = 0; |
| 1320 | menustate->device = NULL; |
| 1321 | } |
| 1322 | break; |
| 1323 | |
| 1324 | case IPT_UI_SELECT: |
| 1325 | { |
| 1326 | if (event->itemref==TAPECMD_STOP) |
| 1327 | cassette_change_state(&menustate->device->device(), CASSETTE_STOPPED, CASSETTE_MASK_UISTATE); |
| 1328 | else |
| 1329 | if (event->itemref==TAPECMD_PLAY) |
| 1330 | cassette_change_state(&menustate->device->device(), CASSETTE_PLAY, CASSETTE_MASK_UISTATE); |
| 1331 | else |
| 1332 | if (event->itemref==TAPECMD_RECORD) |
| 1333 | cassette_change_state(&menustate->device->device(), CASSETTE_RECORD, CASSETTE_MASK_UISTATE); |
| 1334 | else |
| 1335 | if (event->itemref==TAPECMD_REWIND) |
| 1336 | cassette_seek(&menustate->device->device(), -30, SEEK_CUR); |
| 1337 | else |
| 1338 | if (event->itemref==TAPECMD_FAST_FORWARD) |
| 1339 | cassette_seek(&menustate->device->device(), 30, SEEK_CUR); |
| 1340 | else |
| 1341 | if (event->itemref==TAPECMD_SLIDER) |
| 1342 | cassette_seek(&menustate->device->device(), 0, SEEK_SET); |
| 1343 | } |
| 1344 | break; |
| 1345 | } |
| 1346 | } |
| 1347 | } |
| 1348 | |
| 1349 | |
| 1350 | /*------------------------------------------------- |
| 1351 | menu_bitbanger_control - main bitbanger |
| 1352 | control menu |
| 1353 | -------------------------------------------------*/ |
| 1354 | |
| 1355 | void ui_mess_menu_bitbanger_control(running_machine *machine, ui_menu *menu, void *parameter, void *state) |
| 1356 | { |
| 1357 | bitbanger_control_menu_state *menustate; |
| 1358 | const ui_menu_event *event; |
| 1359 | |
| 1360 | /* if no state, allocate some */ |
| 1361 | if (state == NULL) |
| 1362 | state = ui_menu_alloc_state(menu, sizeof(*menustate), NULL); |
| 1363 | menustate = (bitbanger_control_menu_state *) state; |
| 1364 | |
| 1365 | /* do we have to load the device? */ |
| 1366 | if (menustate->device == NULL) |
| 1367 | { |
| 1368 | int index = menustate->index; |
| 1369 | device_image_interface *device = NULL; |
| 1370 | for (bool gotone = machine->m_devicelist.first(device); gotone; gotone = device->next(device)) |
| 1371 | { |
| 1372 | if(device->device().type() == BITBANGER) { |
| 1373 | if (index==0) break; |
| 1374 | index--; |
| 1375 | } |
| 1376 | } |
| 1377 | menustate->device = device; |
| 1378 | ui_menu_reset(menu, (ui_menu_reset_options)0); |
| 1379 | } |
| 1380 | |
| 1381 | /* rebuild the menu */ |
| 1382 | ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION); |
| 1383 | menu_bitbanger_control_populate(machine, menu, (bitbanger_control_menu_state*)state); |
| 1384 | |
| 1385 | /* process the menu */ |
| 1386 | event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT); |
| 1387 | if (event != NULL) |
| 1388 | { |
| 1389 | switch(event->iptkey) |
| 1390 | { |
| 1391 | case IPT_UI_LEFT: |
| 1392 | if (event->itemref==BITBANGERCMD_SELECT) |
| 1393 | { |
| 1394 | /* left arrow - rotate left through cassette devices */ |
| 1395 | if (menustate->index > 0) |
| 1396 | menustate->index--; |
| 1397 | else |
| 1398 | menustate->index = bitbanger_count(machine) - 1; |
| 1399 | menustate->device = NULL; |
| 1400 | } |
| 1401 | else if (event->itemref==BITBANGERCMD_MODE) |
| 1402 | { |
| 1403 | bitbanger_dec_mode(&menustate->device->device(), FALSE); |
| 1404 | } |
| 1405 | else if (event->itemref==BITBANGERCMD_BAUD) |
| 1406 | { |
| 1407 | bitbanger_dec_baud(&menustate->device->device(), FALSE); |
| 1408 | } |
| 1409 | else if (event->itemref==BITBANGERCMD_TUNE) |
| 1410 | { |
| 1411 | bitbanger_dec_tune(&menustate->device->device(), FALSE); |
| 1412 | } |
| 1413 | break; |
| 1414 | |
| 1415 | case IPT_UI_RIGHT: |
| 1416 | if (event->itemref==BITBANGERCMD_SELECT) |
| 1417 | { |
| 1418 | /* right arrow - rotate right through cassette devices */ |
| 1419 | if (menustate->index < bitbanger_count(machine) - 1) |
| 1420 | menustate->index++; |
| 1421 | else |
| 1422 | menustate->index = 0; |
| 1423 | menustate->device = NULL; |
| 1424 | } |
| 1425 | else if (event->itemref==BITBANGERCMD_MODE) |
| 1426 | { |
| 1427 | bitbanger_inc_mode(&menustate->device->device(), FALSE); |
| 1428 | } |
| 1429 | else if (event->itemref==BITBANGERCMD_BAUD) |
| 1430 | { |
| 1431 | bitbanger_inc_baud(&menustate->device->device(), FALSE); |
| 1432 | } |
| 1433 | else if (event->itemref==BITBANGERCMD_TUNE) |
| 1434 | { |
| 1435 | bitbanger_inc_tune(&menustate->device->device(), FALSE); |
| 1436 | } |
| 1437 | break; |
| 1438 | } |
| 1439 | } |
| 1440 | } |
| 1441 | |
src/emu/imagedev/cartslot.h
| r0 | r9928 | |
| 1 | /*************************************************************************** |
| 2 | |
| 3 | Cartrige loading |
| 4 | |
| 5 | ***************************************************************************/ |
| 6 | |
| 7 | #ifndef __CARTSLOT_H__ |
| 8 | #define __CARTSLOT_H__ |
| 9 | |
| 10 | #include "image.h" |
| 11 | #include "multcart.h" |
| 12 | |
| 13 | |
| 14 | /*************************************************************************** |
| 15 | MACROS |
| 16 | ***************************************************************************/ |
| 17 | #define TAG_PCB "pcb" |
| 18 | |
| 19 | #define ROM_CART_LOAD(tag,offset,length,flags) \ |
| 20 | { NULL, tag, offset, length, ROMENTRYTYPE_CARTRIDGE | (flags) }, |
| 21 | |
| 22 | #define ROM_MIRROR 0x01000000 |
| 23 | #define ROM_NOMIRROR 0x00000000 |
| 24 | #define ROM_FULLSIZE 0x02000000 |
| 25 | #define ROM_FILL_FF 0x04000000 |
| 26 | #define ROM_NOCLEAR 0x08000000 |
| 27 | |
| 28 | DECLARE_LEGACY_IMAGE_DEVICE(CARTSLOT, cartslot); |
| 29 | |
| 30 | |
| 31 | |
| 32 | /*************************************************************************** |
| 33 | TYPE DEFINITIONS |
| 34 | ***************************************************************************/ |
| 35 | |
| 36 | typedef struct _cartslot_t cartslot_t; |
| 37 | struct _cartslot_t |
| 38 | { |
| 39 | device_t *pcb_device; |
| 40 | multicart_t *mc; |
| 41 | }; |
| 42 | |
| 43 | |
| 44 | typedef struct _cartslot_pcb_type cartslot_pcb_type; |
| 45 | struct _cartslot_pcb_type |
| 46 | { |
| 47 | const char * name; |
| 48 | device_type devtype; |
| 49 | }; |
| 50 | |
| 51 | |
| 52 | typedef struct _cartslot_config cartslot_config; |
| 53 | struct _cartslot_config |
| 54 | { |
| 55 | const char * extensions; |
| 56 | const char * interface; |
| 57 | int must_be_loaded; |
| 58 | device_start_func device_start; |
| 59 | device_image_load_func device_load; |
| 60 | device_image_unload_func device_unload; |
| 61 | device_image_partialhash_func device_partialhash; |
| 62 | cartslot_pcb_type pcb_types[16]; |
| 63 | }; |
| 64 | |
| 65 | |
| 66 | /*************************************************************************** |
| 67 | FUNCTION PROTOTYPES |
| 68 | ***************************************************************************/ |
| 69 | |
| 70 | /* accesses the PCB associated with this cartslot */ |
| 71 | device_t *cartslot_get_pcb(device_t *device); |
| 72 | |
| 73 | /* accesses a particular socket */ |
| 74 | void *cartslot_get_socket(device_t *device, const char *socket_name); |
| 75 | |
| 76 | /* accesses a particular socket; gets the length of the associated resource */ |
| 77 | int cartslot_get_resource_length(device_t *device, const char *socket_name); |
| 78 | |
| 79 | |
| 80 | /*************************************************************************** |
| 81 | DEVICE CONFIGURATION MACROS |
| 82 | ***************************************************************************/ |
| 83 | |
| 84 | #define MCFG_CARTSLOT_ADD(_tag) \ |
| 85 | MCFG_DEVICE_ADD(_tag, CARTSLOT, 0) \ |
| 86 | |
| 87 | #define MCFG_CARTSLOT_MODIFY(_tag) \ |
| 88 | MCFG_DEVICE_MODIFY(_tag) \ |
| 89 | |
| 90 | #define MCFG_CARTSLOT_EXTENSION_LIST(_extensions) \ |
| 91 | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, extensions, _extensions) |
| 92 | |
| 93 | #define MCFG_CARTSLOT_NOT_MANDATORY \ |
| 94 | MCFG_DEVICE_CONFIG_DATA32(cartslot_config, must_be_loaded, FALSE) |
| 95 | |
| 96 | #define MCFG_CARTSLOT_MANDATORY \ |
| 97 | MCFG_DEVICE_CONFIG_DATA32(cartslot_config, must_be_loaded, TRUE) |
| 98 | |
| 99 | #define MCFG_CARTSLOT_START(_start) \ |
| 100 | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_start, DEVICE_START_NAME(_start)) |
| 101 | |
| 102 | #define MCFG_CARTSLOT_LOAD(_load) \ |
| 103 | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_load, DEVICE_IMAGE_LOAD_NAME(_load)) |
| 104 | |
| 105 | #define MCFG_CARTSLOT_UNLOAD(_unload) \ |
| 106 | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_unload, DEVICE_IMAGE_UNLOAD_NAME(_unload)) |
| 107 | |
| 108 | #define MCFG_CARTSLOT_PARTIALHASH(_partialhash) \ |
| 109 | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_partialhash, _partialhash) |
| 110 | |
| 111 | #define MCFG_CARTSLOT_PCBTYPE(_index, _pcb_type_name, _pcb_devtype) \ |
| 112 | MCFG_DEVICE_CONFIG_DATAPTR_ARRAY_MEMBER(cartslot_config, pcb_types, _index, cartslot_pcb_type, name, _pcb_type_name) \ |
| 113 | MCFG_DEVICE_CONFIG_DATAPTR_ARRAY_MEMBER(cartslot_config, pcb_types, _index, cartslot_pcb_type, devtype, _pcb_devtype) |
| 114 | |
| 115 | #define MCFG_CARTSLOT_INTERFACE(_interface) \ |
| 116 | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, interface, _interface ) |
| 117 | |
| 118 | #define DECLARE_LEGACY_CART_SLOT_DEVICE(name, basename) _DECLARE_LEGACY_DEVICE(name, basename, basename##_device_config, basename##_device, legacy_cart_slot_device_config_base, legacy_cart_slot_device_base) |
| 119 | #define DEFINE_LEGACY_CART_SLOT_DEVICE(name, basename) _DEFINE_LEGACY_DEVICE(name, basename, basename##_device_config, basename##_device, legacy_cart_slot_device_config_base, legacy_cart_slot_device_base) |
| 120 | |
| 121 | // ======================> device_config_cart_slot_interface |
| 122 | |
| 123 | // class representing interface-specific configuration cart_slot |
| 124 | class device_config_cart_slot_interface : public device_config_interface |
| 125 | { |
| 126 | public: |
| 127 | // construction/destruction |
| 128 | device_config_cart_slot_interface(const machine_config &mconfig, device_config &device); |
| 129 | virtual ~device_config_cart_slot_interface(); |
| 130 | }; |
| 131 | |
| 132 | |
| 133 | |
| 134 | // ======================> device_cart_slot_interface |
| 135 | |
| 136 | // class representing interface-specific live cart_slot |
| 137 | class device_cart_slot_interface : public device_interface |
| 138 | { |
| 139 | public: |
| 140 | // construction/destruction |
| 141 | device_cart_slot_interface(running_machine &machine, const device_config &config, device_t &device); |
| 142 | virtual ~device_cart_slot_interface(); |
| 143 | |
| 144 | // configuration access |
| 145 | const device_config_cart_slot_interface &cart_slot_config() const { return m_cart_slot_config; } |
| 146 | protected: |
| 147 | |
| 148 | // configuration |
| 149 | const device_config_cart_slot_interface &m_cart_slot_config; // reference to our device_config_execute_interface |
| 150 | }; |
| 151 | // ======================> legacy_cart_slot_device_config |
| 152 | |
| 153 | // legacy_cart_slot_device_config is a device_config with a cart_slot interface |
| 154 | class legacy_cart_slot_device_config_base : public legacy_device_config_base, |
| 155 | public device_config_cart_slot_interface |
| 156 | { |
| 157 | protected: |
| 158 | // construction/destruction |
| 159 | legacy_cart_slot_device_config_base(const machine_config &mconfig, device_type type, const char *tag, const device_config *owner, UINT32 clock, device_get_config_func get_config); |
| 160 | public: |
| 161 | INT64 get_config_int(UINT32 state) const { return get_legacy_config_int(state); } |
| 162 | genf *get_config_fct(UINT32 state) const { return get_legacy_config_fct(state); } |
| 163 | void *get_config_ptr(UINT32 state) const { return get_legacy_config_ptr(state); } |
| 164 | }; |
| 165 | |
| 166 | |
| 167 | |
| 168 | // ======================> legacy_cart_slot_device |
| 169 | |
| 170 | // legacy_cart_slot_device is a legacy_device_base with a cart_slot interface |
| 171 | class legacy_cart_slot_device_base : public legacy_device_base, |
| 172 | public device_cart_slot_interface |
| 173 | { |
| 174 | protected: |
| 175 | // construction/destruction |
| 176 | legacy_cart_slot_device_base(running_machine &machine, const device_config &config); |
| 177 | |
| 178 | // device_cart_slot_interface overrides |
| 179 | }; |
| 180 | #endif /* __cart_slot_H__ */ |
src/emu/imagedev/flopimg.h
| r0 | r9928 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | flopimg.h |
| 4 | |
| 5 | Floppy disk image abstraction code |
| 6 | |
| 7 | *********************************************************************/ |
| 8 | |
| 9 | #ifndef FLOPIMG_H |
| 10 | #define FLOPIMG_H |
| 11 | |
| 12 | #include "emu.h" |
| 13 | #include "opresolv.h" |
| 14 | |
| 15 | |
| 16 | /*************************************************************************** |
| 17 | |
| 18 | Constants |
| 19 | |
| 20 | ***************************************************************************/ |
| 21 | |
| 22 | #define FLOPPY_FLAGS_READWRITE 0 |
| 23 | #define FLOPPY_FLAGS_READONLY 1 |
| 24 | |
| 25 | |
| 26 | |
| 27 | /*************************************************************************** |
| 28 | |
| 29 | Type definitions |
| 30 | |
| 31 | ***************************************************************************/ |
| 32 | |
| 33 | typedef enum |
| 34 | { |
| 35 | FLOPPY_ERROR_SUCCESS, /* no error */ |
| 36 | FLOPPY_ERROR_INTERNAL, /* fatal internal error */ |
| 37 | FLOPPY_ERROR_UNSUPPORTED, /* this operation is unsupported */ |
| 38 | FLOPPY_ERROR_OUTOFMEMORY, /* ran out of memory */ |
| 39 | FLOPPY_ERROR_SEEKERROR, /* attempted to seek to nonexistant location */ |
| 40 | FLOPPY_ERROR_INVALIDIMAGE, /* this image in invalid */ |
| 41 | FLOPPY_ERROR_READONLY, /* attempt to write to read-only image */ |
| 42 | FLOPPY_ERROR_NOSPACE, |
| 43 | FLOPPY_ERROR_PARAMOUTOFRANGE, |
| 44 | FLOPPY_ERROR_PARAMNOTSPECIFIED |
| 45 | } |
| 46 | floperr_t; |
| 47 | |
| 48 | typedef struct _floppy_image floppy_image; |
| 49 | |
| 50 | struct FloppyCallbacks |
| 51 | { |
| 52 | floperr_t (*read_sector)(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen); |
| 53 | floperr_t (*write_sector)(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam); |
| 54 | floperr_t (*read_indexed_sector)(floppy_image *floppy, int head, int track, int sector_index, void *buffer, size_t buflen); |
| 55 | floperr_t (*write_indexed_sector)(floppy_image *floppy, int head, int track, int sector_index, const void *buffer, size_t buflen, int ddam); |
| 56 | floperr_t (*read_track)(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen); |
| 57 | floperr_t (*write_track)(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen); |
| 58 | floperr_t (*format_track)(floppy_image *floppy, int head, int track, option_resolution *params); |
| 59 | floperr_t (*post_format)(floppy_image *floppy, option_resolution *params); |
| 60 | int (*get_heads_per_disk)(floppy_image *floppy); |
| 61 | int (*get_tracks_per_disk)(floppy_image *floppy); |
| 62 | int (*get_sectors_per_track)(floppy_image *floppy, int head, int track); |
| 63 | UINT32 (*get_track_size)(floppy_image *floppy, int head, int track); |
| 64 | floperr_t (*get_sector_length)(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length); |
| 65 | floperr_t (*get_indexed_sector_info)(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags); |
| 66 | floperr_t (*get_track_data_offset)(floppy_image *floppy, int head, int track, UINT64 *offset); |
| 67 | }; |
| 68 | |
| 69 | |
| 70 | |
| 71 | struct FloppyFormat |
| 72 | { |
| 73 | const char *name; |
| 74 | const char *extensions; |
| 75 | const char *description; |
| 76 | floperr_t (*identify)(floppy_image *floppy, const struct FloppyFormat *format, int *vote); |
| 77 | floperr_t (*construct)(floppy_image *floppy, const struct FloppyFormat *format, option_resolution *params); |
| 78 | const char *param_guidelines; |
| 79 | }; |
| 80 | |
| 81 | #define FLOPPY_IDENTIFY(name) floperr_t name(floppy_image *floppy, const struct FloppyFormat *format, int *vote) |
| 82 | #define FLOPPY_CONSTRUCT(name) floperr_t name(floppy_image *floppy, const struct FloppyFormat *format, option_resolution *params) |
| 83 | |
| 84 | FLOPPY_IDENTIFY(td0_dsk_identify); |
| 85 | FLOPPY_CONSTRUCT(td0_dsk_construct); |
| 86 | |
| 87 | FLOPPY_IDENTIFY(imd_dsk_identify); |
| 88 | FLOPPY_CONSTRUCT(imd_dsk_construct); |
| 89 | |
| 90 | FLOPPY_IDENTIFY(cqm_dsk_identify); |
| 91 | FLOPPY_CONSTRUCT(cqm_dsk_construct); |
| 92 | |
| 93 | FLOPPY_IDENTIFY(dsk_dsk_identify); |
| 94 | FLOPPY_CONSTRUCT(dsk_dsk_construct); |
| 95 | |
| 96 | FLOPPY_IDENTIFY(d88_dsk_identify); |
| 97 | FLOPPY_CONSTRUCT(d88_dsk_construct); |
| 98 | |
| 99 | FLOPPY_IDENTIFY(fdi_dsk_identify); |
| 100 | FLOPPY_CONSTRUCT(fdi_dsk_construct); |
| 101 | |
| 102 | #define FLOPPY_OPTIONS_NAME(name) floppyoptions_##name |
| 103 | |
| 104 | #define FLOPPY_OPTIONS_START(name) \ |
| 105 | const struct FloppyFormat floppyoptions_##name[] = \ |
| 106 | { \ |
| 107 | |
| 108 | #define FLOPPY_OPTIONS_END0 \ |
| 109 | { NULL } \ |
| 110 | }; |
| 111 | |
| 112 | #define FLOPPY_OPTIONS_EXTERN(name) \ |
| 113 | extern const struct FloppyFormat floppyoptions_##name[] \ |
| 114 | |
| 115 | #define FLOPPY_OPTION(name, extensions_, description_, identify_, construct_, ranges_)\ |
| 116 | { #name, extensions_, description_, identify_, construct_, ranges_ }, \ |
| 117 | |
| 118 | #define FLOPPY_OPTIONS_END \ |
| 119 | FLOPPY_OPTION( fdi, "fdi", "Formatted Disk Image", fdi_dsk_identify, fdi_dsk_construct, NULL) \ |
| 120 | FLOPPY_OPTION( td0, "td0", "Teledisk floppy disk image", td0_dsk_identify, td0_dsk_construct, NULL) \ |
| 121 | FLOPPY_OPTION( imd, "imd", "IMD floppy disk image", imd_dsk_identify, imd_dsk_construct, NULL) \ |
| 122 | FLOPPY_OPTION( cqm, "cqm,dsk", "CopyQM floppy disk image", cqm_dsk_identify, cqm_dsk_construct, NULL) \ |
| 123 | FLOPPY_OPTION( dsk, "dsk", "DSK floppy disk image", dsk_dsk_identify, dsk_dsk_construct, NULL) \ |
| 124 | FLOPPY_OPTION( d88, "d77,d88,1dd", "D88 Floppy Disk image", d88_dsk_identify, d88_dsk_construct, NULL) \ |
| 125 | FLOPPY_OPTIONS_END0 |
| 126 | |
| 127 | FLOPPY_OPTIONS_EXTERN(default); |
| 128 | |
| 129 | #define PARAM_END '\0' |
| 130 | #define PARAM_HEADS 'H' |
| 131 | #define PARAM_TRACKS 'T' |
| 132 | #define PARAM_SECTORS 'S' |
| 133 | #define PARAM_SECTOR_LENGTH 'L' |
| 134 | #define PARAM_INTERLEAVE 'I' |
| 135 | #define PARAM_FIRST_SECTOR_ID 'F' |
| 136 | |
| 137 | #define HEADS(range) "H" #range |
| 138 | #define TRACKS(range) "T" #range |
| 139 | #define SECTORS(range) "S" #range |
| 140 | #define SECTOR_LENGTH(range) "L" #range |
| 141 | #define INTERLEAVE(range) "I" #range |
| 142 | #define FIRST_SECTOR_ID(range) "F" #range |
| 143 | |
| 144 | |
| 145 | /*************************************************************************** |
| 146 | |
| 147 | Prototypes |
| 148 | |
| 149 | ***************************************************************************/ |
| 150 | |
| 151 | OPTION_GUIDE_EXTERN(floppy_option_guide); |
| 152 | |
| 153 | /* opening, closing and creating of floppy images */ |
| 154 | floperr_t floppy_open(void *fp, const struct io_procs *procs, const char *extension, const struct FloppyFormat *format, int flags, floppy_image **outfloppy); |
| 155 | floperr_t floppy_open_choices(void *fp, const struct io_procs *procs, const char *extension, const struct FloppyFormat *formats, int flags, floppy_image **outfloppy); |
| 156 | floperr_t floppy_create(void *fp, const struct io_procs *procs, const struct FloppyFormat *format, option_resolution *parameters, floppy_image **outfloppy); |
| 157 | void floppy_close(floppy_image *floppy); |
| 158 | |
| 159 | /* useful for identifying a floppy image */ |
| 160 | floperr_t floppy_identify(void *fp, const struct io_procs *procs, const char *extension, |
| 161 | const struct FloppyFormat *formats, int *identified_format); |
| 162 | |
| 163 | /* functions useful within format constructors */ |
| 164 | void *floppy_tag(floppy_image *floppy); |
| 165 | void *floppy_create_tag(floppy_image *floppy, size_t tagsize); |
| 166 | struct FloppyCallbacks *floppy_callbacks(floppy_image *floppy); |
| 167 | UINT8 floppy_get_filler(floppy_image *floppy); |
| 168 | void floppy_set_filler(floppy_image *floppy, UINT8 filler); |
| 169 | |
| 170 | /* calls for accessing disk image data */ |
| 171 | floperr_t floppy_read_sector(floppy_image *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len); |
| 172 | floperr_t floppy_write_sector(floppy_image *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam); |
| 173 | floperr_t floppy_read_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len); |
| 174 | floperr_t floppy_write_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam); |
| 175 | floperr_t floppy_read_track(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len); |
| 176 | floperr_t floppy_write_track(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len); |
| 177 | floperr_t floppy_read_track_data(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len); |
| 178 | floperr_t floppy_write_track_data(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len); |
| 179 | floperr_t floppy_format_track(floppy_image *floppy, int head, int track, option_resolution *params); |
| 180 | int floppy_get_tracks_per_disk(floppy_image *floppy); |
| 181 | int floppy_get_heads_per_disk(floppy_image *floppy); |
| 182 | UINT32 floppy_get_track_size(floppy_image *floppy, int head, int track); |
| 183 | floperr_t floppy_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length); |
| 184 | floperr_t floppy_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags); |
| 185 | floperr_t floppy_get_sector_count(floppy_image *floppy, int head, int track, int *sector_count); |
| 186 | floperr_t floppy_load_track(floppy_image *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length); |
| 187 | int floppy_is_read_only(floppy_image *floppy); |
| 188 | UINT8 floppy_random_byte(floppy_image *floppy); |
| 189 | |
| 190 | /* accessors for meta information about the image */ |
| 191 | const char *floppy_format_description(floppy_image *floppy); |
| 192 | |
| 193 | /* calls for accessing the raw disk image */ |
| 194 | void floppy_image_read(floppy_image *floppy, void *buffer, UINT64 offset, size_t length); |
| 195 | void floppy_image_write(floppy_image *floppy, const void *buffer, UINT64 offset, size_t length); |
| 196 | void floppy_image_write_filler(floppy_image *floppy, UINT8 filler, UINT64 offset, size_t length); |
| 197 | UINT64 floppy_image_size(floppy_image *floppy); |
| 198 | |
| 199 | /* misc */ |
| 200 | const char *floppy_error(floperr_t err); |
| 201 | |
| 202 | |
| 203 | #endif /* FLOPIMG_H */ |
| 204 | |
src/emu/imagedev/harddriv.c
| r0 | r9928 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | Code to interface the MESS image code with MAME's harddisk core. |
| 4 | |
| 5 | We do not support diff files as it will involve some changes in |
| 6 | the MESS image code. Additionally, the need for diff files comes |
| 7 | from MAME's need for "cannonical" hard drive images. |
| 8 | |
| 9 | Raphael Nabet 2003 |
| 10 | |
| 11 | Update: 23-Feb-2004 - Unlike floppy disks, for which we support |
| 12 | myriad formats on many systems, it is my intention for MESS to |
| 13 | standardize on the CHD file format for hard drives so I made a few |
| 14 | changes to support this |
| 15 | |
| 16 | *********************************************************************/ |
| 17 | |
| 18 | #include "emu.h" |
| 19 | #include "harddisk.h" |
| 20 | #include "harddriv.h" |
| 21 | |
| 22 | |
| 23 | static const char *const error_strings[] = |
| 24 | { |
| 25 | "no error", |
| 26 | "no drive interface", |
| 27 | "out of memory", |
| 28 | "invalid file", |
| 29 | "invalid parameter", |
| 30 | "invalid data", |
| 31 | "file not found", |
| 32 | "requires parent", |
| 33 | "file not writeable", |
| 34 | "read error", |
| 35 | "write error", |
| 36 | "codec error", |
| 37 | "invalid parent", |
| 38 | "hunk out of range", |
| 39 | "decompression error", |
| 40 | "compression error", |
| 41 | "can't create file", |
| 42 | "can't verify file" |
| 43 | "operation not supported", |
| 44 | "can't find metadata", |
| 45 | "invalid metadata size", |
| 46 | "unsupported CHD version" |
| 47 | }; |
| 48 | |
| 49 | static const char *chd_get_error_string(int chderr) |
| 50 | { |
| 51 | if ((chderr < 0 ) || (chderr >= ARRAY_LENGTH(error_strings))) |
| 52 | return NULL; |
| 53 | return error_strings[chderr]; |
| 54 | } |
| 55 | |
| 56 | |
| 57 | |
| 58 | static OPTION_GUIDE_START(mess_hd_option_guide) |
| 59 | OPTION_INT('C', "cylinders", "Cylinders") |
| 60 | OPTION_INT('H', "heads", "Heads") |
| 61 | OPTION_INT('S', "sectors", "Sectors") |
| 62 | OPTION_INT('L', "sectorlength", "Sector Bytes") |
| 63 | OPTION_INT('K', "hunksize", "Hunk Bytes") |
| 64 | OPTION_GUIDE_END |
| 65 | |
| 66 | static const char *mess_hd_option_spec = |
| 67 | "C1-[512]-1024;H1/2/[4]/8;S1-[16]-64;L128/256/[512]/1024;K512/1024/2048/[4096]"; |
| 68 | |
| 69 | |
| 70 | typedef struct _dev_harddisk_t dev_harddisk_t; |
| 71 | struct _dev_harddisk_t |
| 72 | { |
| 73 | const struct harddisk_callback_config *config; |
| 74 | chd_file *chd; |
| 75 | hard_disk_file *hard_disk_handle; |
| 76 | }; |
| 77 | |
| 78 | |
| 79 | INLINE dev_harddisk_t *get_safe_token(device_t *device) |
| 80 | { |
| 81 | assert( device != NULL ); |
| 82 | assert( ( device->type() == HARDDISK ) || |
| 83 | ( device->type() == IDE_HARDDISK ) ); |
| 84 | return (dev_harddisk_t *) downcast<legacy_device_base *>(device)->token(); |
| 85 | } |
| 86 | |
| 87 | |
| 88 | /************************************* |
| 89 | * |
| 90 | * DEVICE_IMAGE_LOAD(mess_hd) |
| 91 | * DEVICE_IMAGE_CREATE(mess_hd) |
| 92 | * |
| 93 | * Device load and create |
| 94 | * |
| 95 | *************************************/ |
| 96 | |
| 97 | static int internal_load_mess_hd(device_image_interface &image, const char *metadata) |
| 98 | { |
| 99 | dev_harddisk_t *harddisk = get_safe_token( &image.device() ); |
| 100 | chd_error err = (chd_error)0; |
| 101 | int is_writeable; |
| 102 | |
| 103 | /* open the CHD file */ |
| 104 | do |
| 105 | { |
| 106 | is_writeable = image.is_writable(); |
| 107 | harddisk->chd = NULL; |
| 108 | err = chd_open_file(image.image_core_file(), is_writeable ? CHD_OPEN_READWRITE : CHD_OPEN_READ, NULL, &harddisk->chd); |
| 109 | |
| 110 | /* special case; if we get CHDERR_FILE_NOT_WRITEABLE, make the |
| 111 | * image read only and repeat */ |
| 112 | if (err == CHDERR_FILE_NOT_WRITEABLE) |
| 113 | image.make_readonly(); |
| 114 | } |
| 115 | while(!harddisk->chd && is_writeable && (err == CHDERR_FILE_NOT_WRITEABLE)); |
| 116 | if (!harddisk->chd) |
| 117 | goto done; |
| 118 | |
| 119 | /* if we created the image and hence, have metadata to set, set the metadata */ |
| 120 | if (metadata) |
| 121 | { |
| 122 | err = chd_set_metadata(harddisk->chd, HARD_DISK_METADATA_TAG, 0, metadata, strlen(metadata) + 1, 0); |
| 123 | if (err != CHDERR_NONE) |
| 124 | goto done; |
| 125 | } |
| 126 | |
| 127 | /* open the hard disk file */ |
| 128 | harddisk->hard_disk_handle = hard_disk_open(harddisk->chd); |
| 129 | if (!harddisk->hard_disk_handle) |
| 130 | goto done; |
| 131 | |
| 132 | done: |
| 133 | if (err) |
| 134 | { |
| 135 | /* if we had an error, close out the CHD */ |
| 136 | if (harddisk->chd != NULL) |
| 137 | { |
| 138 | chd_close(harddisk->chd); |
| 139 | harddisk->chd = NULL; |
| 140 | } |
| 141 | |
| 142 | image.seterror(IMAGE_ERROR_UNSPECIFIED, chd_get_error_string(err)); |
| 143 | } |
| 144 | return err ? IMAGE_INIT_FAIL : IMAGE_INIT_PASS; |
| 145 | } |
| 146 | |
| 147 | |
| 148 | |
| 149 | static DEVICE_IMAGE_LOAD( mess_hd ) |
| 150 | { |
| 151 | dev_harddisk_t *harddisk = get_safe_token( image ); |
| 152 | int our_result; |
| 153 | |
| 154 | our_result = internal_load_mess_hd(image, NULL); |
| 155 | |
| 156 | /* Check if there is an image_load callback defined */ |
| 157 | if ( harddisk->config && harddisk->config->device_image_load ) |
| 158 | { |
| 159 | /* Let the override do some additional work/checks */ |
| 160 | our_result = harddisk->config->device_image_load( image ); |
| 161 | } |
| 162 | return our_result; |
| 163 | } |
| 164 | |
| 165 | |
| 166 | static DEVICE_IMAGE_CREATE( mess_hd ) |
| 167 | { |
| 168 | int err; |
| 169 | char metadata[256]; |
| 170 | UINT32 sectorsize, hunksize; |
| 171 | UINT32 cylinders, heads, sectors, totalsectors; |
| 172 | |
| 173 | cylinders = option_resolution_lookup_int(create_args, 'C'); |
| 174 | heads = option_resolution_lookup_int(create_args, 'H'); |
| 175 | sectors = option_resolution_lookup_int(create_args, 'S'); |
| 176 | sectorsize = option_resolution_lookup_int(create_args, 'L'); |
| 177 | hunksize = option_resolution_lookup_int(create_args, 'K'); |
| 178 | |
| 179 | totalsectors = cylinders * heads * sectors; |
| 180 | |
| 181 | /* create the CHD file */ |
| 182 | err = chd_create_file(image.image_core_file(), (UINT64)totalsectors * (UINT64)sectorsize, hunksize, CHDCOMPRESSION_NONE, NULL); |
| 183 | if (err != CHDERR_NONE) |
| 184 | goto error; |
| 185 | |
| 186 | sprintf(metadata, HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, sectorsize); |
| 187 | return internal_load_mess_hd(image, metadata); |
| 188 | |
| 189 | error: |
| 190 | return IMAGE_INIT_FAIL; |
| 191 | } |
| 192 | |
| 193 | |
| 194 | |
| 195 | /************************************* |
| 196 | * |
| 197 | * DEVICE_IMAGE_UNLOAD(mess_hd) |
| 198 | * |
| 199 | * Device unload |
| 200 | * |
| 201 | *************************************/ |
| 202 | |
| 203 | static DEVICE_IMAGE_UNLOAD( mess_hd ) |
| 204 | { |
| 205 | dev_harddisk_t *harddisk = get_safe_token( image ); |
| 206 | |
| 207 | /* Check if there is an image_unload callback defined */ |
| 208 | if ( harddisk->config && harddisk->config->device_image_unload ) |
| 209 | { |
| 210 | harddisk->config->device_image_unload( image ); |
| 211 | } |
| 212 | |
| 213 | if (harddisk->hard_disk_handle != NULL) |
| 214 | { |
| 215 | hard_disk_close(harddisk->hard_disk_handle); |
| 216 | harddisk->hard_disk_handle = NULL; |
| 217 | } |
| 218 | |
| 219 | if (harddisk->chd != NULL) |
| 220 | { |
| 221 | chd_close(harddisk->chd); |
| 222 | harddisk->chd = NULL; |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | |
| 227 | /************************************* |
| 228 | * |
| 229 | * Get the MESS/MAME hard disk handle (from the src/harddisk.c core) |
| 230 | * after an image has been opened with the mess_hd core |
| 231 | * |
| 232 | *************************************/ |
| 233 | |
| 234 | hard_disk_file *mess_hd_get_hard_disk_file(device_t *device) |
| 235 | { |
| 236 | dev_harddisk_t *harddisk = get_safe_token( device ); |
| 237 | |
| 238 | return harddisk->hard_disk_handle; |
| 239 | } |
| 240 | |
| 241 | |
| 242 | /************************************* |
| 243 | * |
| 244 | * Get the MESS/MAME CHD file (from the src/chd.c core) |
| 245 | * after an image has been opened with the mess_hd core |
| 246 | * |
| 247 | *************************************/ |
| 248 | |
| 249 | chd_file *mess_hd_get_chd_file(device_t *device) |
| 250 | { |
| 251 | chd_file *result = NULL; |
| 252 | hard_disk_file *hd_file; |
| 253 | |
| 254 | if (device) |
| 255 | { |
| 256 | hd_file = mess_hd_get_hard_disk_file(device); |
| 257 | if (hd_file) |
| 258 | result = hard_disk_get_chd(hd_file); |
| 259 | } |
| 260 | return result; |
| 261 | } |
| 262 | |
| 263 | |
| 264 | /*------------------------------------------------- |
| 265 | DEVICE_START(mess_hd) |
| 266 | -------------------------------------------------*/ |
| 267 | |
| 268 | static DEVICE_START(mess_hd) |
| 269 | { |
| 270 | dev_harddisk_t *harddisk = get_safe_token( device ); |
| 271 | |
| 272 | harddisk->config = (const harddisk_callback_config*)device->baseconfig().static_config(); |
| 273 | harddisk->chd = NULL; |
| 274 | harddisk->hard_disk_handle = NULL; |
| 275 | } |
| 276 | |
| 277 | |
| 278 | /*------------------------------------------------- |
| 279 | DEVICE_GET_INFO(mess_hd) |
| 280 | -------------------------------------------------*/ |
| 281 | |
| 282 | DEVICE_GET_INFO(mess_hd) |
| 283 | { |
| 284 | switch( state ) |
| 285 | { |
| 286 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 287 | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(dev_harddisk_t); break; |
| 288 | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; |
| 289 | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_HARDDISK; break; |
| 290 | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 291 | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break; |
| 292 | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 0; break; |
| 293 | case DEVINFO_INT_IMAGE_CREATE_OPTCOUNT: info->i = 1; break; |
| 294 | |
| 295 | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 296 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(mess_hd); break; |
| 297 | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(mess_hd); break; |
| 298 | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(mess_hd); break; |
| 299 | case DEVINFO_FCT_IMAGE_CREATE: info->f = (genf *) DEVICE_IMAGE_CREATE_NAME(mess_hd); break; |
| 300 | case DEVINFO_PTR_IMAGE_CREATE_OPTGUIDE: info->p = (void *) mess_hd_option_guide; break; |
| 301 | case DEVINFO_PTR_IMAGE_CREATE_OPTSPEC+0: info->p = (void *) mess_hd_option_spec; break; |
| 302 | |
| 303 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 304 | case DEVINFO_STR_NAME: strcpy(info->s, "Harddisk"); break; |
| 305 | case DEVINFO_STR_FAMILY: strcpy(info->s, "Harddisk"); break; |
| 306 | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 307 | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, "chd,hd"); break; |
| 308 | case DEVINFO_STR_IMAGE_CREATE_OPTNAME+0: strcpy(info->s, "chd"); break; |
| 309 | case DEVINFO_STR_IMAGE_CREATE_OPTDESC+0: strcpy(info->s, "MAME/MESS CHD Hard drive"); break; |
| 310 | case DEVINFO_STR_IMAGE_CREATE_OPTEXTS+0: strcpy(info->s, "chd,hd"); break; |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | |
| 315 | static DEVICE_START(mess_ide) |
| 316 | { |
| 317 | /* old code from idedrive.c */ |
| 318 | #ifdef UNUSED_FUNCTION |
| 319 | int which_bus, which_address; |
| 320 | struct ide_interface *intf; |
| 321 | device_start_func parent_init; |
| 322 | |
| 323 | /* get the basics */ |
| 324 | ide_get_params(device, &which_bus, &which_address, &intf, &parent_init, NULL, NULL); |
| 325 | |
| 326 | /* call the parent init function */ |
| 327 | parent_init(device); |
| 328 | |
| 329 | /* configure IDE */ |
| 330 | /* FIXME IDE */ |
| 331 | /* ide_controller_init_custom(which_bus, intf, NULL); */ |
| 332 | #endif |
| 333 | } |
| 334 | |
| 335 | |
| 336 | static DEVICE_IMAGE_LOAD(mess_ide) |
| 337 | { |
| 338 | /* old code from idedrive.c */ |
| 339 | #ifdef UNUSED_FUNCTION |
| 340 | int result, which_bus, which_address; |
| 341 | struct ide_interface *intf; |
| 342 | device_image_load_func parent_load; |
| 343 | |
| 344 | /* get the basics */ |
| 345 | ide_get_params(image, &which_bus, &which_address, &intf, NULL, &parent_load, NULL); |
| 346 | |
| 347 | /* call the parent load function */ |
| 348 | result = parent_load(image); |
| 349 | if (result != IMAGE_INIT_PASS) |
| 350 | return result; |
| 351 | |
| 352 | /* configure IDE */ |
| 353 | /* FIXME IDE */ |
| 354 | /* ide_controller_init_custom(which_bus, intf, mess_hd_get_chd_file(image)); */ |
| 355 | /* ide_controller_reset(which_bus); */ |
| 356 | return IMAGE_INIT_PASS; |
| 357 | #endif |
| 358 | return device_load_mess_hd( image ); |
| 359 | } |
| 360 | |
| 361 | |
| 362 | static DEVICE_IMAGE_UNLOAD(mess_ide) |
| 363 | { |
| 364 | /* old code from idedrive.c */ |
| 365 | #ifdef UNUSED_FUNCTION |
| 366 | int which_bus, which_address; |
| 367 | struct ide_interface *intf; |
| 368 | device_image_unload_func parent_unload; |
| 369 | |
| 370 | /* get the basics */ |
| 371 | ide_get_params(image, &which_bus, &which_address, &intf, NULL, NULL, &parent_unload); |
| 372 | |
| 373 | /* call the parent unload function */ |
| 374 | parent_unload(image); |
| 375 | |
| 376 | /* configure IDE */ |
| 377 | /* FIXME IDE */ |
| 378 | /* ide_controller_init_custom(which_bus, intf, NULL); */ |
| 379 | /* ide_controller_reset(which_bus); */ |
| 380 | #endif |
| 381 | device_unload_mess_hd( image ); |
| 382 | } |
| 383 | |
| 384 | |
| 385 | DEVICE_GET_INFO(mess_ide) |
| 386 | { |
| 387 | switch( state ) |
| 388 | { |
| 389 | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 390 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(mess_ide); break; |
| 391 | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(mess_ide); break; |
| 392 | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(mess_ide); break; |
| 393 | |
| 394 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 395 | case DEVINFO_STR_NAME: strcpy(info->s, "IDE harddisk"); break; |
| 396 | case DEVINFO_STR_IMAGE_INSTANCE_NAME: strcpy(info->s, "ideharddrive"); break; |
| 397 | case DEVINFO_STR_IMAGE_BRIEF_INSTANCE_NAME: strcpy(info->s, "idehd"); break; |
| 398 | |
| 399 | default: DEVICE_GET_INFO_CALL( mess_hd ); break; |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | DEFINE_LEGACY_IMAGE_DEVICE(HARDDISK, mess_hd); |
| 404 | DEFINE_LEGACY_IMAGE_DEVICE(IDE_HARDDISK, mess_ide); |
src/emu/imagedev/cassette.c
| r0 | r9928 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | cassette.c |
| 4 | |
| 5 | MESS interface to the cassette image abstraction code |
| 6 | |
| 7 | *********************************************************************/ |
| 8 | |
| 9 | #include "emu.h" |
| 10 | #include "imageutl.h" |
| 11 | #include "cassette.h" |
| 12 | #include "cassimg.h" |
| 13 | #include "ui.h" |
| 14 | |
| 15 | |
| 16 | #define CASSETTE_TAG "cassette" |
| 17 | #define ANIMATION_FPS 1 |
| 18 | #define ANIMATION_FRAMES 4 |
| 19 | |
| 20 | #define VERBOSE 0 |
| 21 | #define LOG(x) do { if (VERBOSE) logerror x; } while (0) |
| 22 | |
| 23 | |
| 24 | typedef struct _dev_cassette_t dev_cassette_t; |
| 25 | struct _dev_cassette_t |
| 26 | { |
| 27 | const cassette_config *config; |
| 28 | cassette_image *cassette; |
| 29 | cassette_state state; |
| 30 | double position; |
| 31 | double position_time; |
| 32 | INT32 value; |
| 33 | }; |
| 34 | |
| 35 | |
| 36 | /* Default cassette_config for drivers only wav files */ |
| 37 | const cassette_config default_cassette_config = |
| 38 | { |
| 39 | cassette_default_formats, |
| 40 | NULL, |
| 41 | CASSETTE_PLAY, |
| 42 | NULL |
| 43 | }; |
| 44 | |
| 45 | |
| 46 | INLINE dev_cassette_t *get_safe_token(device_t *device) |
| 47 | { |
| 48 | assert( device != NULL ); |
| 49 | assert( device->type() == CASSETTE ); |
| 50 | return (dev_cassette_t *) downcast<legacy_device_base *>(device)->token(); |
| 51 | } |
| 52 | |
| 53 | |
| 54 | /********************************************************************* |
| 55 | cassette IO |
| 56 | *********************************************************************/ |
| 57 | |
| 58 | INLINE int cassette_is_motor_on(device_t *device) |
| 59 | { |
| 60 | dev_cassette_t *cassette = get_safe_token( device ); |
| 61 | |
| 62 | if ((cassette->state & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED) |
| 63 | return FALSE; |
| 64 | if ((cassette->state & CASSETTE_MASK_MOTOR) != CASSETTE_MOTOR_ENABLED) |
| 65 | return FALSE; |
| 66 | return TRUE; |
| 67 | } |
| 68 | |
| 69 | |
| 70 | |
| 71 | static void cassette_update(device_t *device) |
| 72 | { |
| 73 | dev_cassette_t *cassette = get_safe_token( device ); |
| 74 | double cur_time = attotime_to_double(timer_get_time(device->machine)); |
| 75 | |
| 76 | if (cassette_is_motor_on(device)) |
| 77 | { |
| 78 | double new_position = cassette->position + (cur_time - cassette->position_time); |
| 79 | |
| 80 | switch(cassette->state & CASSETTE_MASK_UISTATE) { |
| 81 | case CASSETTE_RECORD: |
| 82 | cassette_put_sample(cassette->cassette, 0, cassette->position, new_position - cassette->position, cassette->value); |
| 83 | break; |
| 84 | |
| 85 | case CASSETTE_PLAY: |
| 86 | if ( cassette->cassette ) |
| 87 | { |
| 88 | cassette_get_sample(cassette->cassette, 0, new_position, 0.0, &cassette->value); |
| 89 | /* See if reached end of tape */ |
| 90 | double length = cassette_get_length(device); |
| 91 | if (new_position > length) |
| 92 | { |
| 93 | cassette->state = (cassette_state)(( cassette->state & ~CASSETTE_MASK_UISTATE ) | CASSETTE_STOPPED); |
| 94 | new_position = length; |
| 95 | } |
| 96 | } |
| 97 | break; |
| 98 | } |
| 99 | cassette->position = new_position; |
| 100 | } |
| 101 | cassette->position_time = cur_time; |
| 102 | } |
| 103 | |
| 104 | |
| 105 | |
| 106 | cassette_state cassette_get_state(device_t *device) |
| 107 | { |
| 108 | dev_cassette_t *cassette = get_safe_token( device ); |
| 109 | return cassette->state; |
| 110 | } |
| 111 | |
| 112 | |
| 113 | |
| 114 | void cassette_change_state(device_t *device, cassette_state state, cassette_state mask) |
| 115 | { |
| 116 | dev_cassette_t *cassette = get_safe_token( device ); |
| 117 | cassette_state new_state; |
| 118 | |
| 119 | new_state = cassette->state; |
| 120 | new_state = (cassette_state)(new_state & ~mask); |
| 121 | new_state = (cassette_state)(new_state | (state & mask)); |
| 122 | |
| 123 | if (new_state != cassette->state) |
| 124 | { |
| 125 | cassette_update(device); |
| 126 | cassette->state = new_state; |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | |
| 131 | |
| 132 | void cassette_set_state(device_t *device, cassette_state state) |
| 133 | { |
| 134 | cassette_change_state(device, state, (cassette_state)(~0)); |
| 135 | } |
| 136 | |
| 137 | |
| 138 | |
| 139 | double cassette_input(device_t *device) |
| 140 | { |
| 141 | dev_cassette_t *cassette = get_safe_token( device ); |
| 142 | INT32 sample; |
| 143 | double double_value; |
| 144 | |
| 145 | cassette_update(device); |
| 146 | sample = cassette->value; |
| 147 | double_value = sample / ((double) 0x7FFFFFFF); |
| 148 | |
| 149 | LOG(("cassette_input(): time_index=%g value=%g\n", cassette->position, double_value)); |
| 150 | |
| 151 | return double_value; |
| 152 | } |
| 153 | |
| 154 | |
| 155 | |
| 156 | void cassette_output(device_t *device, double value) |
| 157 | { |
| 158 | dev_cassette_t *cassette = get_safe_token( device ); |
| 159 | |
| 160 | if (((cassette->state & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD) && (cassette->value != value)) |
| 161 | { |
| 162 | cassette_update(device); |
| 163 | |
| 164 | value = MIN(value, 1.0); |
| 165 | value = MAX(value, -1.0); |
| 166 | |
| 167 | cassette->value = (INT32) (value * 0x7FFFFFFF); |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | |
| 172 | |
| 173 | cassette_image *cassette_get_image(device_t *device) |
| 174 | { |
| 175 | dev_cassette_t *cassette = get_safe_token( device ); |
| 176 | |
| 177 | return cassette->cassette; |
| 178 | } |
| 179 | |
| 180 | |
| 181 | |
| 182 | double cassette_get_position(device_t *device) |
| 183 | { |
| 184 | dev_cassette_t *cassette = get_safe_token( device ); |
| 185 | double position; |
| 186 | |
| 187 | position = cassette->position; |
| 188 | |
| 189 | if (cassette_is_motor_on(device)) |
| 190 | position += attotime_to_double(timer_get_time(device->machine)) - cassette->position_time; |
| 191 | return position; |
| 192 | } |
| 193 | |
| 194 | |
| 195 | |
| 196 | double cassette_get_length(device_t *device) |
| 197 | { |
| 198 | dev_cassette_t *cassette = get_safe_token( device ); |
| 199 | struct CassetteInfo info; |
| 200 | |
| 201 | cassette_get_info(cassette->cassette, &info); |
| 202 | return ((double) info.sample_count) / info.sample_frequency; |
| 203 | } |
| 204 | |
| 205 | |
| 206 | |
| 207 | void cassette_seek(device_t *device, double time, int origin) |
| 208 | { |
| 209 | dev_cassette_t *cassette = get_safe_token( device ); |
| 210 | |
| 211 | double length; |
| 212 | |
| 213 | cassette_update(device); |
| 214 | |
| 215 | length = cassette_get_length(device); |
| 216 | |
| 217 | switch(origin) { |
| 218 | case SEEK_SET: |
| 219 | break; |
| 220 | |
| 221 | case SEEK_END: |
| 222 | time += length; |
| 223 | break; |
| 224 | |
| 225 | case SEEK_CUR: |
| 226 | time += cassette_get_position(device); |
| 227 | break; |
| 228 | } |
| 229 | |
| 230 | /* clip position into legal bounds */ |
| 231 | if (time < 0) |
| 232 | time = 0; |
| 233 | else |
| 234 | if (time > length) |
| 235 | time = length; |
| 236 | |
| 237 | cassette->position = time; |
| 238 | } |
| 239 | |
| 240 | |
| 241 | |
| 242 | /********************************************************************* |
| 243 | cassette device init/load/unload/specify |
| 244 | *********************************************************************/ |
| 245 | |
| 246 | static DEVICE_START( cassette ) |
| 247 | { |
| 248 | dev_cassette_t *cassette = get_safe_token( device ); |
| 249 | |
| 250 | /* set to default state */ |
| 251 | cassette->config = (const cassette_config*)device->baseconfig().static_config(); |
| 252 | cassette->cassette = NULL; |
| 253 | cassette->state = cassette->config->default_state; |
| 254 | } |
| 255 | |
| 256 | |
| 257 | |
| 258 | static DEVICE_IMAGE_LOAD( cassette ) |
| 259 | { |
| 260 | device_t *device = &image.device(); |
| 261 | dev_cassette_t *cassette = get_safe_token( device ); |
| 262 | casserr_t err; |
| 263 | int cassette_flags; |
| 264 | const struct CassetteFormat * const *formats; |
| 265 | const struct CassetteOptions *create_opts; |
| 266 | const char *extension; |
| 267 | int is_writable; |
| 268 | |
| 269 | /* figure out the cassette format */ |
| 270 | formats = cassette->config->formats; |
| 271 | |
| 272 | if (image.has_been_created()) |
| 273 | { |
| 274 | /* creating an image */ |
| 275 | create_opts = cassette->config->create_opts; |
| 276 | err = cassette_create(device->machine, (void *) &image, &image_ioprocs, &wavfile_format, create_opts, CASSETTE_FLAG_READWRITE|CASSETTE_FLAG_SAVEONEXIT, &cassette->cassette); |
| 277 | if (err) |
| 278 | goto error; |
| 279 | } |
| 280 | else |
| 281 | { |
| 282 | /* opening an image */ |
| 283 | do |
| 284 | { |
| 285 | is_writable = image.is_writable(); |
| 286 | cassette_flags = is_writable ? (CASSETTE_FLAG_READWRITE|CASSETTE_FLAG_SAVEONEXIT) : CASSETTE_FLAG_READONLY; |
| 287 | extension = image.filetype(); |
| 288 | err = cassette_open_choices(device->machine,(void *) &image, &image_ioprocs, extension, formats, cassette_flags, &cassette->cassette); |
| 289 | |
| 290 | /* this is kind of a hack */ |
| 291 | if (err && is_writable) |
| 292 | image.make_readonly(); |
| 293 | } |
| 294 | while(err && is_writable); |
| 295 | |
| 296 | if (err) |
| 297 | goto error; |
| 298 | } |
| 299 | |
| 300 | /* set to default state, but only change the UI state */ |
| 301 | cassette_change_state(device, cassette->config->default_state, CASSETTE_MASK_UISTATE); |
| 302 | |
| 303 | /* reset the position */ |
| 304 | cassette->position = 0.0; |
| 305 | cassette->position_time = attotime_to_double(timer_get_time(device->machine)); |
| 306 | |
| 307 | return IMAGE_INIT_PASS; |
| 308 | |
| 309 | error: |
| 310 | return IMAGE_INIT_FAIL; |
| 311 | } |
| 312 | |
| 313 | |
| 314 | |
| 315 | static DEVICE_IMAGE_UNLOAD( cassette ) |
| 316 | { |
| 317 | device_t *device = &image.device(); |
| 318 | dev_cassette_t *cassette = get_safe_token( device ); |
| 319 | |
| 320 | /* if we are recording, write the value to the image */ |
| 321 | if ((cassette->state & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD) |
| 322 | cassette_update(device); |
| 323 | |
| 324 | /* close out the cassette */ |
| 325 | cassette_close(cassette->cassette); |
| 326 | cassette->cassette = NULL; |
| 327 | |
| 328 | /* set to default state, but only change the UI state */ |
| 329 | cassette_change_state(device, CASSETTE_STOPPED, CASSETTE_MASK_UISTATE); |
| 330 | } |
| 331 | |
| 332 | |
| 333 | |
| 334 | /* |
| 335 | display a small tape icon, with the current position in the tape image |
| 336 | */ |
| 337 | static DEVICE_IMAGE_DISPLAY(cassette) |
| 338 | { |
| 339 | device_t *device = &image.device(); |
| 340 | char buf[65]; |
| 341 | float x, y; |
| 342 | int n; |
| 343 | double position, length; |
| 344 | cassette_state uistate; |
| 345 | device_t *dev; |
| 346 | static const UINT8 shapes[8] = { 0x2d, 0x5c, 0x7c, 0x2f, 0x2d, 0x20, 0x20, 0x20 }; |
| 347 | |
| 348 | /* abort if we should not be showing the image */ |
| 349 | if (!image.exists()) |
| 350 | return; |
| 351 | if (!cassette_is_motor_on(device)) |
| 352 | return; |
| 353 | |
| 354 | /* figure out where we are in the cassette */ |
| 355 | position = cassette_get_position(device); |
| 356 | length = cassette_get_length(device); |
| 357 | uistate = (cassette_state)(cassette_get_state(device) & CASSETTE_MASK_UISTATE); |
| 358 | |
| 359 | /* choose a location on the screen */ |
| 360 | x = 0.2f; |
| 361 | y = 0.5f; |
| 362 | |
| 363 | dev = device->machine->m_devicelist.first(CASSETTE ); |
| 364 | |
| 365 | while ( dev && strcmp( dev->tag(), device->tag() ) ) |
| 366 | { |
| 367 | y += 1; |
| 368 | dev = dev->typenext(); |
| 369 | } |
| 370 | |
| 371 | y *= ui_get_line_height(*device->machine) + 2.0f * UI_BOX_TB_BORDER; |
| 372 | /* choose which frame of the animation we are at */ |
| 373 | n = ((int) position / ANIMATION_FPS) % ANIMATION_FRAMES; |
| 374 | /* Since you can have anything in a BDF file, we will use crude ascii characters instead */ |
| 375 | snprintf(buf, ARRAY_LENGTH(buf), "%c%c %c %02d:%02d (%04d) [%02d:%02d (%04d)]", |
| 376 | #if 0 |
| 377 | /* THE ANIMATION HASN'T WORKED SINCE 0.114 - LEFT HERE FOR REFERENCE */ |
| 378 | /* NEVER SEEN THE PLAY / RECORD ICONS */ |
| 379 | /* character pairs 2-3, 4-5, 6-7, 8-9 form little tape cassette images */ |
| 380 | n * 2 + 2, /* cassette icon left */ |
| 381 | n * 2 + 3, /* cassette icon right */ |
| 382 | (uistate == CASSETTE_PLAY) ? 16 : 14, /* play or record icon */ |
| 383 | #else |
| 384 | shapes[n], /* cassette icon left */ |
| 385 | shapes[n|4], /* cassette icon right */ |
| 386 | (uistate == CASSETTE_PLAY) ? 0x50 : 0x52, /* play (P) or record (R) */ |
| 387 | #endif |
| 388 | ((int) position / 60), |
| 389 | ((int) position % 60), |
| 390 | (int) position, |
| 391 | ((int) length / 60), |
| 392 | ((int) length % 60), |
| 393 | (int) length); |
| 394 | |
| 395 | /* draw the cassette */ |
| 396 | ui_draw_text_box(&device->machine->render().ui_container(), buf, JUSTIFY_LEFT, x, y, UI_BACKGROUND_COLOR); |
| 397 | } |
| 398 | |
| 399 | /*------------------------------------------------- |
| 400 | DEVICE_IMAGE_SOFTLIST_LOAD(cassette) |
| 401 | -------------------------------------------------*/ |
| 402 | static DEVICE_IMAGE_SOFTLIST_LOAD(cassette) |
| 403 | { |
| 404 | return image.load_software(swlist, swname, start_entry); |
| 405 | } |
| 406 | |
| 407 | DEVICE_GET_INFO(cassette) |
| 408 | { |
| 409 | switch( state ) |
| 410 | { |
| 411 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 412 | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(dev_cassette_t); break; |
| 413 | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; |
| 414 | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_CASSETTE; break; |
| 415 | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 416 | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break; |
| 417 | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break; |
| 418 | |
| 419 | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 420 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(cassette); break; |
| 421 | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(cassette); break; |
| 422 | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(cassette); break; |
| 423 | case DEVINFO_FCT_IMAGE_DISPLAY: info->f = (genf *) DEVICE_IMAGE_DISPLAY_NAME(cassette); break; |
| 424 | case DEVINFO_FCT_IMAGE_SOFTLIST_LOAD: info->f = (genf *) DEVICE_IMAGE_SOFTLIST_LOAD_NAME(cassette); break; |
| 425 | |
| 426 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 427 | case DEVINFO_STR_NAME: strcpy(info->s, "Cassette"); break; |
| 428 | case DEVINFO_STR_FAMILY: strcpy(info->s, "Cassette"); break; |
| 429 | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 430 | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: |
| 431 | if ( device && device->static_config() ) |
| 432 | { |
| 433 | const struct CassetteFormat * const *formats = ((cassette_config *)device->static_config())->formats; |
| 434 | int i; |
| 435 | |
| 436 | /* set up a temporary string */ |
| 437 | info->s[0] = '\0'; |
| 438 | |
| 439 | for ( i = 0; formats[i]; i++ ) |
| 440 | image_specify_extension( info->s, 256, formats[i]->extensions ); |
| 441 | } |
| 442 | break; |
| 443 | case DEVINFO_STR_IMAGE_INTERFACE: |
| 444 | if ( device && device->static_config() && ((cassette_config *)device->static_config())->interface) |
| 445 | { |
| 446 | strcpy(info->s, ((cassette_config *)device->static_config())->interface ); |
| 447 | } |
| 448 | break; |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | DEFINE_LEGACY_IMAGE_DEVICE(CASSETTE, cassette); |
src/emu/imagedev/flopdrv.c
| r0 | r9928 | |
| 1 | /* |
| 2 | This code handles the floppy drives. |
| 3 | All FDD actions should be performed using these functions. |
| 4 | |
| 5 | The functions are emulated and a disk image is used. |
| 6 | |
| 7 | Real disk operation: |
| 8 | - set unit id |
| 9 | |
| 10 | TODO: |
| 11 | - Override write protect if disk image has been opened in read mode |
| 12 | */ |
| 13 | |
| 14 | #include "emu.h" |
| 15 | #include "imageutl.h" |
| 16 | #include "flopdrv.h" |
| 17 | |
| 18 | #define VERBOSE 0 |
| 19 | #define LOG(x) do { if (VERBOSE) logerror x; } while (0) |
| 20 | |
| 21 | /*************************************************************************** |
| 22 | CONSTANTS |
| 23 | ***************************************************************************/ |
| 24 | |
| 25 | #define FLOPDRVTAG "flopdrv" |
| 26 | #define LOG_FLOPPY 0 |
| 27 | |
| 28 | /*************************************************************************** |
| 29 | TYPE DEFINITIONS |
| 30 | ***************************************************************************/ |
| 31 | |
| 32 | typedef struct _floppy_drive floppy_drive; |
| 33 | struct _floppy_drive |
| 34 | { |
| 35 | /* callbacks */ |
| 36 | devcb_resolved_write_line out_idx_func; |
| 37 | devcb_resolved_read_line in_mon_func; |
| 38 | devcb_resolved_write_line out_tk00_func; |
| 39 | devcb_resolved_write_line out_wpt_func; |
| 40 | devcb_resolved_write_line out_rdy_func; |
| 41 | devcb_resolved_write_line out_dskchg_func; |
| 42 | |
| 43 | /* state of input lines */ |
| 44 | int drtn; /* direction */ |
| 45 | int stp; /* step */ |
| 46 | int wtg; /* write gate */ |
| 47 | int mon; /* motor on */ |
| 48 | |
| 49 | /* state of output lines */ |
| 50 | int idx; /* index pulse */ |
| 51 | int tk00; /* track 00 */ |
| 52 | int wpt; /* write protect */ |
| 53 | int rdy; /* ready */ |
| 54 | int dskchg; /* disk changed */ |
| 55 | |
| 56 | /* drive select logic */ |
| 57 | int drive_id; |
| 58 | int active; |
| 59 | |
| 60 | const floppy_config *config; |
| 61 | |
| 62 | /* flags */ |
| 63 | int flags; |
| 64 | /* maximum track allowed */ |
| 65 | int max_track; |
| 66 | /* num sides */ |
| 67 | int num_sides; |
| 68 | /* current track - this may or may not relate to the present cylinder number |
| 69 | stored by the fdc */ |
| 70 | int current_track; |
| 71 | |
| 72 | /* index pulse timer */ |
| 73 | void *index_timer; |
| 74 | /* index pulse callback */ |
| 75 | void (*index_pulse_callback)(device_t *controller,device_t *image, int state); |
| 76 | /* rotation per minute => gives index pulse frequency */ |
| 77 | float rpm; |
| 78 | |
| 79 | void (*ready_state_change_callback)(device_t *controller,device_t *img, int state); |
| 80 | |
| 81 | int id_index; |
| 82 | |
| 83 | device_t *controller; |
| 84 | |
| 85 | floppy_image *floppy; |
| 86 | int track; |
| 87 | void (*load_proc)(device_image_interface &image); |
| 88 | void (*unload_proc)(device_image_interface &image); |
| 89 | void *custom_data; |
| 90 | int floppy_drive_type; |
| 91 | }; |
| 92 | |
| 93 | |
| 94 | typedef struct _floppy_error_map floppy_error_map; |
| 95 | struct _floppy_error_map |
| 96 | { |
| 97 | floperr_t ferr; |
| 98 | image_error_t ierr; |
| 99 | const char *message; |
| 100 | }; |
| 101 | |
| 102 | |
| 103 | |
| 104 | /*************************************************************************** |
| 105 | GLOBAL VARIABLES |
| 106 | ***************************************************************************/ |
| 107 | |
| 108 | static const floppy_error_map errmap[] = |
| 109 | { |
| 110 | { FLOPPY_ERROR_SUCCESS, IMAGE_ERROR_SUCCESS }, |
| 111 | { FLOPPY_ERROR_INTERNAL, IMAGE_ERROR_INTERNAL }, |
| 112 | { FLOPPY_ERROR_UNSUPPORTED, IMAGE_ERROR_UNSUPPORTED }, |
| 113 | { FLOPPY_ERROR_OUTOFMEMORY, IMAGE_ERROR_OUTOFMEMORY }, |
| 114 | { FLOPPY_ERROR_INVALIDIMAGE, IMAGE_ERROR_INVALIDIMAGE } |
| 115 | }; |
| 116 | |
| 117 | /*************************************************************************** |
| 118 | IMPLEMENTATION |
| 119 | ***************************************************************************/ |
| 120 | DECLARE_LEGACY_IMAGE_DEVICE(FLOPPY_APPLE, apple525); |
| 121 | DECLARE_LEGACY_IMAGE_DEVICE(FLOPPY_SONY, sonydriv); |
| 122 | |
| 123 | INLINE floppy_drive *get_safe_token(device_t *device) |
| 124 | { |
| 125 | assert( device != NULL ); |
| 126 | assert( device->type() == FLOPPY || device->type() == FLOPPY_APPLE || device->type() == FLOPPY_SONY); |
| 127 | return (floppy_drive *) downcast<legacy_device_base *>(device)->token(); |
| 128 | } |
| 129 | |
| 130 | floppy_image *flopimg_get_image(device_t *image) |
| 131 | { |
| 132 | return get_safe_token(image)->floppy; |
| 133 | } |
| 134 | |
| 135 | void *flopimg_get_custom_data(device_t *image) |
| 136 | { |
| 137 | floppy_drive *flopimg = get_safe_token( image ); |
| 138 | return flopimg->custom_data; |
| 139 | } |
| 140 | |
| 141 | void flopimg_alloc_custom_data(device_t *image,void *custom) |
| 142 | { |
| 143 | floppy_drive *flopimg = get_safe_token( image ); |
| 144 | flopimg->custom_data = custom; |
| 145 | } |
| 146 | |
| 147 | static int flopimg_get_sectors_per_track(device_t *image, int side) |
| 148 | { |
| 149 | floperr_t err; |
| 150 | int sector_count; |
| 151 | floppy_drive *flopimg = get_safe_token( image ); |
| 152 | |
| 153 | if (!flopimg || !flopimg->floppy) |
| 154 | return 0; |
| 155 | |
| 156 | err = floppy_get_sector_count(flopimg->floppy, side, flopimg->track, §or_count); |
| 157 | if (err) |
| 158 | return 0; |
| 159 | return sector_count; |
| 160 | } |
| 161 | |
| 162 | static void flopimg_get_id_callback(device_t *image, chrn_id *id, int id_index, int side) |
| 163 | { |
| 164 | floppy_drive *flopimg; |
| 165 | int cylinder, sector, N; |
| 166 | unsigned long flags; |
| 167 | UINT32 sector_length; |
| 168 | |
| 169 | flopimg = get_safe_token( image ); |
| 170 | if (!flopimg || !flopimg->floppy) |
| 171 | return; |
| 172 | |
| 173 | floppy_get_indexed_sector_info(flopimg->floppy, side, flopimg->track, id_index, &cylinder, &side, §or, §or_length, &flags); |
| 174 | |
| 175 | N = compute_log2(sector_length); |
| 176 | |
| 177 | id->C = cylinder; |
| 178 | id->H = side; |
| 179 | id->R = sector; |
| 180 | id->data_id = id_index; |
| 181 | id->flags = flags; |
| 182 | id->N = ((N >= 7) && (N <= 10)) ? N - 7 : 0; |
| 183 | } |
| 184 | |
| 185 | static void log_readwrite(const char *name, int head, int track, int sector, const char *buf, int length) |
| 186 | { |
| 187 | char membuf[1024]; |
| 188 | int i; |
| 189 | for (i = 0; i < length; i++) |
| 190 | sprintf(membuf + i*2, "%02x", (int) (UINT8) buf[i]); |
| 191 | logerror("%s: head=%i track=%i sector=%i buffer='%s'\n", name, head, track, sector, membuf); |
| 192 | } |
| 193 | |
| 194 | static void floppy_drive_set_geometry_absolute(device_t *img, int tracks, int sides) |
| 195 | { |
| 196 | floppy_drive *pDrive = get_safe_token( img ); |
| 197 | pDrive->max_track = tracks; |
| 198 | pDrive->num_sides = sides; |
| 199 | } |
| 200 | |
| 201 | void floppy_drive_set_geometry(device_t *img, floppy_type_t type) |
| 202 | { |
| 203 | floppy_drive_set_geometry_absolute(img, type.max_track_number, type.head_number); |
| 204 | } |
| 205 | |
| 206 | static TIMER_CALLBACK(floppy_drive_index_callback); |
| 207 | |
| 208 | /* this is called on device init */ |
| 209 | static void floppy_drive_init(device_t *img) |
| 210 | { |
| 211 | floppy_drive *pDrive = get_safe_token( img ); |
| 212 | |
| 213 | /* initialise flags */ |
| 214 | pDrive->flags = 0; |
| 215 | pDrive->index_pulse_callback = NULL; |
| 216 | pDrive->ready_state_change_callback = NULL; |
| 217 | pDrive->index_timer = timer_alloc(img->machine, floppy_drive_index_callback, (void *) img); |
| 218 | pDrive->idx = 0; |
| 219 | |
| 220 | floppy_drive_set_geometry(img, ((floppy_config*)img->baseconfig().static_config())->floppy_type); |
| 221 | |
| 222 | /* initialise id index - not so important */ |
| 223 | pDrive->id_index = 0; |
| 224 | /* initialise track */ |
| 225 | pDrive->current_track = 0; |
| 226 | |
| 227 | /* default RPM */ |
| 228 | pDrive->rpm = 300; |
| 229 | |
| 230 | pDrive->controller = NULL; |
| 231 | |
| 232 | pDrive->custom_data = NULL; |
| 233 | |
| 234 | pDrive->floppy_drive_type = FLOPPY_TYPE_REGULAR; |
| 235 | } |
| 236 | |
| 237 | /* index pulses at rpm/60 Hz, and stays high 1/20th of time */ |
| 238 | static void floppy_drive_index_func(device_t *img) |
| 239 | { |
| 240 | floppy_drive *drive = get_safe_token( img ); |
| 241 | |
| 242 | double ms = 1000.0 / (drive->rpm / 60.0); |
| 243 | |
| 244 | if (drive->idx) |
| 245 | { |
| 246 | drive->idx = 0; |
| 247 | timer_adjust_oneshot((emu_timer*)drive->index_timer, double_to_attotime(ms*19/20/1000.0), 0); |
| 248 | } |
| 249 | else |
| 250 | { |
| 251 | drive->idx = 1; |
| 252 | timer_adjust_oneshot((emu_timer*)drive->index_timer, double_to_attotime(ms/20/1000.0), 0); |
| 253 | } |
| 254 | |
| 255 | devcb_call_write_line(&drive->out_idx_func, drive->idx); |
| 256 | |
| 257 | if (drive->index_pulse_callback) |
| 258 | drive->index_pulse_callback(drive->controller, img, drive->idx); |
| 259 | } |
| 260 | |
| 261 | static TIMER_CALLBACK(floppy_drive_index_callback) |
| 262 | { |
| 263 | device_t *image = (device_t *) ptr; |
| 264 | floppy_drive_index_func(image); |
| 265 | } |
| 266 | |
| 267 | /*************************************************************************/ |
| 268 | /* IO_FLOPPY device functions */ |
| 269 | |
| 270 | /* set flag state */ |
| 271 | void floppy_drive_set_flag_state(device_t *img, int flag, int state) |
| 272 | { |
| 273 | floppy_drive *drv = get_safe_token( img ); |
| 274 | int prev_state; |
| 275 | int new_state; |
| 276 | |
| 277 | /* get old state */ |
| 278 | prev_state = drv->flags & flag; |
| 279 | |
| 280 | /* set new state */ |
| 281 | drv->flags &= ~flag; |
| 282 | if (state) |
| 283 | drv->flags |= flag; |
| 284 | |
| 285 | /* get new state */ |
| 286 | new_state = drv->flags & flag; |
| 287 | |
| 288 | /* changed state? */ |
| 289 | if (prev_state ^ new_state) |
| 290 | { |
| 291 | if (flag & FLOPPY_DRIVE_READY) |
| 292 | { |
| 293 | /* trigger state change callback */ |
| 294 | devcb_call_write_line(&drv->out_rdy_func, new_state ? ASSERT_LINE : CLEAR_LINE); |
| 295 | |
| 296 | if (drv->ready_state_change_callback) |
| 297 | drv->ready_state_change_callback(drv->controller, img, new_state); |
| 298 | } |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | /* for pc, drive is always ready, for amstrad,pcw,spectrum it is only ready under |
| 303 | a fixed set of circumstances */ |
| 304 | /* use this to set ready state of drive */ |
| 305 | void floppy_drive_set_ready_state(device_t *img, int state, int flag) |
| 306 | { |
| 307 | floppy_drive *drive = get_safe_token(img); |
| 308 | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 309 | if (flag) |
| 310 | { |
| 311 | /* set ready only if drive is present, disk is in the drive, |
| 312 | and disk motor is on - for Amstrad, Spectrum and PCW*/ |
| 313 | |
| 314 | /* drive present? */ |
| 315 | /* disk inserted? */ |
| 316 | if (image->exists()) |
| 317 | { |
| 318 | if (drive->mon == CLEAR_LINE) |
| 319 | { |
| 320 | /* set state */ |
| 321 | floppy_drive_set_flag_state(img, FLOPPY_DRIVE_READY, state); |
| 322 | return; |
| 323 | } |
| 324 | } |
| 325 | floppy_drive_set_flag_state(img, FLOPPY_DRIVE_READY, 0); |
| 326 | } |
| 327 | else |
| 328 | { |
| 329 | /* force ready state - for PC driver */ |
| 330 | floppy_drive_set_flag_state(img, FLOPPY_DRIVE_READY, state); |
| 331 | } |
| 332 | } |
| 333 | |
| 334 | /* get flag state */ |
| 335 | int floppy_drive_get_flag_state(device_t *img, int flag) |
| 336 | { |
| 337 | floppy_drive *drv = get_safe_token( img ); |
| 338 | int drive_flags; |
| 339 | int flags; |
| 340 | |
| 341 | flags = 0; |
| 342 | |
| 343 | drive_flags = drv->flags; |
| 344 | |
| 345 | /* these flags are independant of a real drive/disk image */ |
| 346 | flags |= drive_flags & (FLOPPY_DRIVE_READY | FLOPPY_DRIVE_INDEX); |
| 347 | |
| 348 | flags &= flag; |
| 349 | |
| 350 | return flags; |
| 351 | } |
| 352 | |
| 353 | |
| 354 | void floppy_drive_seek(device_t *img, signed int signed_tracks) |
| 355 | { |
| 356 | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 357 | floppy_drive *pDrive; |
| 358 | |
| 359 | pDrive = get_safe_token( img ); |
| 360 | |
| 361 | LOG(("seek from: %d delta: %d\n",pDrive->current_track, signed_tracks)); |
| 362 | |
| 363 | /* update position */ |
| 364 | pDrive->current_track+=signed_tracks; |
| 365 | |
| 366 | if (pDrive->current_track<0) |
| 367 | { |
| 368 | pDrive->current_track = 0; |
| 369 | } |
| 370 | else |
| 371 | if (pDrive->current_track>=pDrive->max_track) |
| 372 | { |
| 373 | pDrive->current_track = pDrive->max_track-1; |
| 374 | } |
| 375 | |
| 376 | /* set track 0 flag */ |
| 377 | pDrive->tk00 = (pDrive->current_track == 0) ? CLEAR_LINE : ASSERT_LINE; |
| 378 | devcb_call_write_line(&pDrive->out_tk00_func, pDrive->tk00); |
| 379 | |
| 380 | /* clear disk changed flag */ |
| 381 | pDrive->dskchg = ASSERT_LINE; |
| 382 | //devcb_call_write_line(&flopimg->out_dskchg_func, flopimg->dskchg); |
| 383 | |
| 384 | /* inform disk image of step operation so it can cache information */ |
| 385 | if (image->exists()) |
| 386 | pDrive->track = pDrive->current_track; |
| 387 | |
| 388 | pDrive->id_index = 0; |
| 389 | } |
| 390 | |
| 391 | |
| 392 | /* this is not accurate. But it will do for now */ |
| 393 | int floppy_drive_get_next_id(device_t *img, int side, chrn_id *id) |
| 394 | { |
| 395 | floppy_drive *pDrive; |
| 396 | int spt; |
| 397 | |
| 398 | pDrive = get_safe_token( img ); |
| 399 | |
| 400 | /* get sectors per track */ |
| 401 | spt = flopimg_get_sectors_per_track(img, side); |
| 402 | |
| 403 | /* set index */ |
| 404 | if ((pDrive->id_index==(spt-1)) || (spt==0)) |
| 405 | { |
| 406 | floppy_drive_set_flag_state(img, FLOPPY_DRIVE_INDEX, 1); |
| 407 | } |
| 408 | else |
| 409 | { |
| 410 | floppy_drive_set_flag_state(img, FLOPPY_DRIVE_INDEX, 0); |
| 411 | } |
| 412 | |
| 413 | /* get id */ |
| 414 | if (spt!=0) |
| 415 | { |
| 416 | flopimg_get_id_callback(img, id, pDrive->id_index, side); |
| 417 | } |
| 418 | |
| 419 | pDrive->id_index++; |
| 420 | if (spt!=0) |
| 421 | pDrive->id_index %= spt; |
| 422 | else |
| 423 | pDrive->id_index = 0; |
| 424 | |
| 425 | return (spt == 0) ? 0 : 1; |
| 426 | } |
| 427 | |
| 428 | void floppy_drive_read_track_data_info_buffer(device_t *img, int side, void *ptr, int *length ) |
| 429 | { |
| 430 | floppy_drive *flopimg; |
| 431 | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 432 | |
| 433 | if (image->exists()) |
| 434 | { |
| 435 | flopimg = get_safe_token( img ); |
| 436 | if (!flopimg || !flopimg->floppy) |
| 437 | return; |
| 438 | |
| 439 | floppy_read_track_data(flopimg->floppy, side, flopimg->track, ptr, *length); |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | void floppy_drive_write_track_data_info_buffer(device_t *img, int side, const void *ptr, int *length ) |
| 444 | { |
| 445 | floppy_drive *flopimg; |
| 446 | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 447 | |
| 448 | if (image->exists()) |
| 449 | { |
| 450 | flopimg = get_safe_token( img ); |
| 451 | if (!flopimg || !flopimg->floppy) |
| 452 | return; |
| 453 | |
| 454 | floppy_write_track_data(flopimg->floppy, side, flopimg->track, ptr, *length); |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | void floppy_drive_format_sector(device_t *img, int side, int sector_index,int c,int h, int r, int n, int filler) |
| 459 | { |
| 460 | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 461 | |
| 462 | if (image->exists()) |
| 463 | { |
| 464 | /* if (drv->interface_.format_sector) |
| 465 | drv->interface_.format_sector(img, side, sector_index,c, h, r, n, filler);*/ |
| 466 | } |
| 467 | } |
| 468 | |
| 469 | void floppy_drive_read_sector_data(device_t *img, int side, int index1, void *ptr, int length) |
| 470 | { |
| 471 | floppy_drive *flopimg; |
| 472 | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 473 | |
| 474 | if (image->exists()) |
| 475 | { |
| 476 | flopimg = get_safe_token( img ); |
| 477 | if (!flopimg || !flopimg->floppy) |
| 478 | return; |
| 479 | |
| 480 | floppy_read_indexed_sector(flopimg->floppy, side, flopimg->track, index1, 0, ptr, length); |
| 481 | |
| 482 | if (LOG_FLOPPY) |
| 483 | log_readwrite("sector_read", side, flopimg->track, index1, (const char *)ptr, length); |
| 484 | |
| 485 | } |
| 486 | } |
| 487 | |
| 488 | void floppy_drive_write_sector_data(device_t *img, int side, int index1, const void *ptr,int length, int ddam) |
| 489 | { |
| 490 | floppy_drive *flopimg; |
| 491 | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 492 | |
| 493 | if (image->exists()) |
| 494 | { |
| 495 | flopimg = get_safe_token( img ); |
| 496 | if (!flopimg || !flopimg->floppy) |
| 497 | return; |
| 498 | |
| 499 | if (LOG_FLOPPY) |
| 500 | log_readwrite("sector_write", side, flopimg->track, index1, (const char *)ptr, length); |
| 501 | |
| 502 | floppy_write_indexed_sector(flopimg->floppy, side, flopimg->track, index1, 0, ptr, length, ddam); |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | void floppy_install_load_proc(device_t *image, void (*proc)(device_image_interface &image)) |
| 507 | { |
| 508 | floppy_drive *flopimg = get_safe_token( image ); |
| 509 | flopimg->load_proc = proc; |
| 510 | } |
| 511 | |
| 512 | void floppy_install_unload_proc(device_t *image, void (*proc)(device_image_interface &image)) |
| 513 | { |
| 514 | floppy_drive *flopimg = get_safe_token( image ); |
| 515 | flopimg->unload_proc = proc; |
| 516 | } |
| 517 | |
| 518 | /* set the callback for the index pulse */ |
| 519 | void floppy_drive_set_index_pulse_callback(device_t *img, void (*callback)(device_t *controller,device_t *image, int state)) |
| 520 | { |
| 521 | floppy_drive *pDrive = get_safe_token( img ); |
| 522 | pDrive->index_pulse_callback = callback; |
| 523 | } |
| 524 | |
| 525 | |
| 526 | void floppy_drive_set_ready_state_change_callback(device_t *img, void (*callback)(device_t *controller,device_t *img, int state)) |
| 527 | { |
| 528 | floppy_drive *pDrive = get_safe_token( img ); |
| 529 | pDrive->ready_state_change_callback = callback; |
| 530 | } |
| 531 | |
| 532 | int floppy_drive_get_current_track(device_t *img) |
| 533 | { |
| 534 | floppy_drive *drv = get_safe_token( img ); |
| 535 | return drv->current_track; |
| 536 | } |
| 537 | |
| 538 | UINT64 floppy_drive_get_current_track_size(device_t *img, int head) |
| 539 | { |
| 540 | floppy_drive *drv = get_safe_token( img ); |
| 541 | int size = 0; |
| 542 | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 543 | |
| 544 | if (image->exists()) |
| 545 | { |
| 546 | size = floppy_get_track_size(drv->floppy, head, drv->current_track); |
| 547 | } |
| 548 | |
| 549 | return size; |
| 550 | } |
| 551 | |
| 552 | void floppy_drive_set_rpm(device_t *img, float rpm) |
| 553 | { |
| 554 | floppy_drive *drv = get_safe_token( img ); |
| 555 | drv->rpm = rpm; |
| 556 | } |
| 557 | |
| 558 | void floppy_drive_set_controller(device_t *img, device_t *controller) |
| 559 | { |
| 560 | floppy_drive *drv = get_safe_token( img ); |
| 561 | drv->controller = controller; |
| 562 | } |
| 563 | |
| 564 | /* ----------------------------------------------------------------------- */ |
| 565 | DEVICE_START( floppy ) |
| 566 | { |
| 567 | floppy_drive *floppy = get_safe_token( device ); |
| 568 | floppy->config = (const floppy_config*)device->baseconfig().static_config(); |
| 569 | floppy_drive_init(device); |
| 570 | |
| 571 | floppy->drive_id = floppy_get_drive(device); |
| 572 | floppy->active = FALSE; |
| 573 | |
| 574 | /* resolve callbacks */ |
| 575 | devcb_resolve_write_line(&floppy->out_idx_func, &floppy->config->out_idx_func, device); |
| 576 | devcb_resolve_read_line(&floppy->in_mon_func, &floppy->config->in_mon_func, device); |
| 577 | devcb_resolve_write_line(&floppy->out_tk00_func, &floppy->config->out_tk00_func, device); |
| 578 | devcb_resolve_write_line(&floppy->out_wpt_func, &floppy->config->out_wpt_func, device); |
| 579 | devcb_resolve_write_line(&floppy->out_rdy_func, &floppy->config->out_rdy_func, device); |
| 580 | // devcb_resolve_write_line(&floppy->out_dskchg_func, &floppy->config->out_dskchg_func, device); |
| 581 | |
| 582 | /* by default we are not write-protected */ |
| 583 | floppy->wpt = ASSERT_LINE; |
| 584 | devcb_call_write_line(&floppy->out_wpt_func, floppy->wpt); |
| 585 | |
| 586 | /* not at track 0 */ |
| 587 | floppy->tk00 = ASSERT_LINE; |
| 588 | devcb_call_write_line(&floppy->out_tk00_func, floppy->tk00); |
| 589 | |
| 590 | /* motor off */ |
| 591 | floppy->mon = ASSERT_LINE; |
| 592 | |
| 593 | /* disk changed */ |
| 594 | floppy->dskchg = CLEAR_LINE; |
| 595 | // devcb_call_write_line(&floppy->out_dskchg_func, floppy->dskchg); |
| 596 | } |
| 597 | |
| 598 | static int internal_floppy_device_load(device_image_interface *image, int create_format, option_resolution *create_args) |
| 599 | { |
| 600 | floperr_t err; |
| 601 | floppy_drive *flopimg; |
| 602 | const struct FloppyFormat *floppy_options; |
| 603 | int floppy_flags, i; |
| 604 | const char *extension; |
| 605 | |
| 606 | /* look up instance data */ |
| 607 | flopimg = get_safe_token( &image->device() ); |
| 608 | |
| 609 | /* figure out the floppy options */ |
| 610 | floppy_options = ((floppy_config*)image->device().baseconfig().static_config())->formats; |
| 611 | |
| 612 | if (image->has_been_created()) |
| 613 | { |
| 614 | /* creating an image */ |
| 615 | assert(create_format >= 0); |
| 616 | err = floppy_create((void *) image, &image_ioprocs, &floppy_options[create_format], create_args, &flopimg->floppy); |
| 617 | if (err) |
| 618 | goto error; |
| 619 | } |
| 620 | else |
| 621 | { |
| 622 | /* opening an image */ |
| 623 | floppy_flags = image->is_writable() ? FLOPPY_FLAGS_READWRITE : FLOPPY_FLAGS_READONLY; |
| 624 | extension = image->filetype(); |
| 625 | err = floppy_open_choices((void *) image, &image_ioprocs, extension, floppy_options, floppy_flags, &flopimg->floppy); |
| 626 | if (err) |
| 627 | goto error; |
| 628 | } |
| 629 | if (floppy_callbacks(flopimg->floppy)->get_heads_per_disk && floppy_callbacks(flopimg->floppy)->get_tracks_per_disk) |
| 630 | { |
| 631 | floppy_drive_set_geometry_absolute(&image->device(), |
| 632 | floppy_get_tracks_per_disk(flopimg->floppy), |
| 633 | floppy_get_heads_per_disk(flopimg->floppy)); |
| 634 | } |
| 635 | return IMAGE_INIT_PASS; |
| 636 | |
| 637 | error: |
| 638 | for (i = 0; i < ARRAY_LENGTH(errmap); i++) |
| 639 | { |
| 640 | if (err == errmap[i].ferr) |
| 641 | image->seterror(errmap[i].ierr, errmap[i].message); |
| 642 | } |
| 643 | return IMAGE_INIT_FAIL; |
| 644 | } |
| 645 | |
| 646 | static TIMER_CALLBACK( set_wpt ) |
| 647 | { |
| 648 | floppy_drive *flopimg = (floppy_drive *)ptr; |
| 649 | |
| 650 | flopimg->wpt = param; |
| 651 | devcb_call_write_line(&flopimg->out_wpt_func, param); |
| 652 | } |
| 653 | |
| 654 | DEVICE_IMAGE_LOAD( floppy ) |
| 655 | { |
| 656 | floppy_drive *flopimg; |
| 657 | int retVal = internal_floppy_device_load(&image, -1, NULL); |
| 658 | flopimg = get_safe_token( &image.device() ); |
| 659 | if (retVal==IMAGE_INIT_PASS) { |
| 660 | /* if we have one of our hacky unload procs, call it */ |
| 661 | if (flopimg->load_proc) |
| 662 | flopimg->load_proc(image); |
| 663 | } |
| 664 | |
| 665 | /* push disk halfway into drive */ |
| 666 | flopimg->wpt = CLEAR_LINE; |
| 667 | devcb_call_write_line(&flopimg->out_wpt_func, flopimg->wpt); |
| 668 | |
| 669 | /* set timer for disk load */ |
| 670 | int next_wpt; |
| 671 | |
| 672 | if (image.is_writable()) |
| 673 | next_wpt = ASSERT_LINE; |
| 674 | else |
| 675 | next_wpt = CLEAR_LINE; |
| 676 | |
| 677 | timer_set(image.device().machine, ATTOTIME_IN_MSEC(250), flopimg, next_wpt, set_wpt); |
| 678 | |
| 679 | return retVal; |
| 680 | } |
| 681 | |
| 682 | DEVICE_IMAGE_CREATE( floppy ) |
| 683 | { |
| 684 | return internal_floppy_device_load(&image, create_format, create_args); |
| 685 | } |
| 686 | |
| 687 | DEVICE_IMAGE_UNLOAD( floppy ) |
| 688 | { |
| 689 | floppy_drive *flopimg = get_safe_token( &image.device() ); |
| 690 | if (flopimg->unload_proc) |
| 691 | flopimg->unload_proc(image); |
| 692 | |
| 693 | floppy_close(flopimg->floppy); |
| 694 | flopimg->floppy = NULL; |
| 695 | |
| 696 | /* disk changed */ |
| 697 | flopimg->dskchg = CLEAR_LINE; |
| 698 | //devcb_call_write_line(&flopimg->out_dskchg_func, flopimg->dskchg); |
| 699 | |
| 700 | /* pull disk halfway out of drive */ |
| 701 | flopimg->wpt = CLEAR_LINE; |
| 702 | devcb_call_write_line(&flopimg->out_wpt_func, flopimg->wpt); |
| 703 | |
| 704 | /* set timer for disk eject */ |
| 705 | timer_set(image.device().machine, ATTOTIME_IN_MSEC(250), flopimg, ASSERT_LINE, set_wpt); |
| 706 | } |
| 707 | |
| 708 | device_t *floppy_get_device(running_machine *machine,int drive) |
| 709 | { |
| 710 | switch(drive) { |
| 711 | case 0 : return machine->device(FLOPPY_0); |
| 712 | case 1 : return machine->device(FLOPPY_1); |
| 713 | case 2 : return machine->device(FLOPPY_2); |
| 714 | case 3 : return machine->device(FLOPPY_3); |
| 715 | } |
| 716 | return NULL; |
| 717 | } |
| 718 | |
| 719 | int floppy_get_drive_type(device_t *image) |
| 720 | { |
| 721 | floppy_drive *flopimg = get_safe_token( image ); |
| 722 | return flopimg->floppy_drive_type; |
| 723 | } |
| 724 | |
| 725 | void floppy_set_type(device_t *image,int ftype) |
| 726 | { |
| 727 | floppy_drive *flopimg = get_safe_token( image ); |
| 728 | flopimg->floppy_drive_type = ftype; |
| 729 | } |
| 730 | |
| 731 | device_t *floppy_get_device_by_type(running_machine *machine,int ftype,int drive) |
| 732 | { |
| 733 | int i; |
| 734 | int cnt = 0; |
| 735 | for (i=0;i<4;i++) { |
| 736 | device_t *disk = floppy_get_device(machine,i); |
| 737 | if (floppy_get_drive_type(disk)==ftype) { |
| 738 | if (cnt==drive) { |
| 739 | return disk; |
| 740 | } |
| 741 | cnt++; |
| 742 | } |
| 743 | } |
| 744 | return NULL; |
| 745 | } |
| 746 | |
| 747 | int floppy_get_drive(device_t *image) |
| 748 | { |
| 749 | int drive =0; |
| 750 | if (strcmp(image->tag(), FLOPPY_0) == 0) drive = 0; |
| 751 | if (strcmp(image->tag(), FLOPPY_1) == 0) drive = 1; |
| 752 | if (strcmp(image->tag(), FLOPPY_2) == 0) drive = 2; |
| 753 | if (strcmp(image->tag(), FLOPPY_3) == 0) drive = 3; |
| 754 | return drive; |
| 755 | } |
| 756 | |
| 757 | int floppy_get_drive_by_type(device_t *image,int ftype) |
| 758 | { |
| 759 | int i,drive =0; |
| 760 | for (i=0;i<4;i++) { |
| 761 | device_t *disk = floppy_get_device(image->machine,i); |
| 762 | if (floppy_get_drive_type(disk)==ftype) { |
| 763 | if (image==disk) { |
| 764 | return drive; |
| 765 | } |
| 766 | drive++; |
| 767 | } |
| 768 | } |
| 769 | return drive; |
| 770 | } |
| 771 | |
| 772 | int floppy_get_count(running_machine *machine) |
| 773 | { |
| 774 | int cnt = 0; |
| 775 | if (machine->device(FLOPPY_0)) cnt++; |
| 776 | if (machine->device(FLOPPY_1)) cnt++; |
| 777 | if (machine->device(FLOPPY_2)) cnt++; |
| 778 | if (machine->device(FLOPPY_3)) cnt++; |
| 779 | return cnt; |
| 780 | } |
| 781 | |
| 782 | |
| 783 | /* drive select 0 */ |
| 784 | WRITE_LINE_DEVICE_HANDLER( floppy_ds0_w ) |
| 785 | { |
| 786 | floppy_drive *drive = get_safe_token(device); |
| 787 | |
| 788 | if (state == CLEAR_LINE) |
| 789 | drive->active = (drive->drive_id == 0); |
| 790 | } |
| 791 | |
| 792 | /* drive select 1 */ |
| 793 | WRITE_LINE_DEVICE_HANDLER( floppy_ds1_w ) |
| 794 | { |
| 795 | floppy_drive *drive = get_safe_token(device); |
| 796 | |
| 797 | if (state == CLEAR_LINE) |
| 798 | drive->active = (drive->drive_id == 1); |
| 799 | } |
| 800 | |
| 801 | /* drive select 2 */ |
| 802 | WRITE_LINE_DEVICE_HANDLER( floppy_ds2_w ) |
| 803 | { |
| 804 | floppy_drive *drive = get_safe_token(device); |
| 805 | |
| 806 | if (state == CLEAR_LINE) |
| 807 | drive->active = (drive->drive_id == 2); |
| 808 | } |
| 809 | |
| 810 | /* drive select 3 */ |
| 811 | WRITE_LINE_DEVICE_HANDLER( floppy_ds3_w ) |
| 812 | { |
| 813 | floppy_drive *drive = get_safe_token(device); |
| 814 | |
| 815 | if (state == CLEAR_LINE) |
| 816 | drive->active = (drive->drive_id == 3); |
| 817 | } |
| 818 | |
| 819 | /* shortcut to write all four ds lines */ |
| 820 | WRITE8_DEVICE_HANDLER( floppy_ds_w ) |
| 821 | { |
| 822 | floppy_ds0_w(device, BIT(data, 0)); |
| 823 | floppy_ds1_w(device, BIT(data, 1)); |
| 824 | floppy_ds2_w(device, BIT(data, 2)); |
| 825 | floppy_ds3_w(device, BIT(data, 3)); |
| 826 | } |
| 827 | |
| 828 | /* motor on, active low */ |
| 829 | WRITE_LINE_DEVICE_HANDLER( floppy_mon_w ) |
| 830 | { |
| 831 | floppy_drive *drive = get_safe_token(device); |
| 832 | device_image_interface *image = dynamic_cast<device_image_interface *>(device); |
| 833 | /* force off if there is no attached image */ |
| 834 | if (!image->exists()) |
| 835 | state = ASSERT_LINE; |
| 836 | |
| 837 | /* off -> on */ |
| 838 | if (drive->mon && state == CLEAR_LINE) |
| 839 | { |
| 840 | drive->idx = 0; |
| 841 | floppy_drive_index_func(device); |
| 842 | } |
| 843 | |
| 844 | /* on -> off */ |
| 845 | else if (drive->mon == CLEAR_LINE && state) |
| 846 | timer_adjust_oneshot((emu_timer*)drive->index_timer, attotime_zero, 0); |
| 847 | |
| 848 | drive->mon = state; |
| 849 | } |
| 850 | |
| 851 | /* direction */ |
| 852 | WRITE_LINE_DEVICE_HANDLER( floppy_drtn_w ) |
| 853 | { |
| 854 | floppy_drive *drive = get_safe_token(device); |
| 855 | drive->drtn = state; |
| 856 | } |
| 857 | |
| 858 | /* write data */ |
| 859 | WRITE_LINE_DEVICE_HANDLER( floppy_wtd_w ) |
| 860 | { |
| 861 | |
| 862 | } |
| 863 | |
| 864 | /* step */ |
| 865 | WRITE_LINE_DEVICE_HANDLER( floppy_stp_w ) |
| 866 | { |
| 867 | floppy_drive *drive = get_safe_token(device); |
| 868 | |
| 869 | /* move head one track when going from high to low and write gate is high */ |
| 870 | if (drive->active && drive->stp && state == CLEAR_LINE && drive->wtg) |
| 871 | { |
| 872 | /* move head according to the direction line */ |
| 873 | if (drive->drtn) |
| 874 | { |
| 875 | /* move head outward */ |
| 876 | if (drive->current_track > 0) |
| 877 | drive->current_track--; |
| 878 | |
| 879 | /* are we at track 0 now? */ |
| 880 | drive->tk00 = (drive->current_track == 0) ? CLEAR_LINE : ASSERT_LINE; |
| 881 | } |
| 882 | else |
| 883 | { |
| 884 | /* move head inward */ |
| 885 | if (drive->current_track < drive->max_track) |
| 886 | drive->current_track++; |
| 887 | |
| 888 | /* we can't be at track 0 here, so reset the line */ |
| 889 | drive->tk00 = ASSERT_LINE; |
| 890 | } |
| 891 | |
| 892 | /* update track 0 line with new status */ |
| 893 | devcb_call_write_line(&drive->out_tk00_func, drive->tk00); |
| 894 | } |
| 895 | |
| 896 | drive->stp = state; |
| 897 | } |
| 898 | |
| 899 | /* write gate */ |
| 900 | WRITE_LINE_DEVICE_HANDLER( floppy_wtg_w ) |
| 901 | { |
| 902 | floppy_drive *drive = get_safe_token(device); |
| 903 | drive->wtg = state; |
| 904 | } |
| 905 | |
| 906 | /* write protect signal, active low */ |
| 907 | READ_LINE_DEVICE_HANDLER( floppy_wpt_r ) |
| 908 | { |
| 909 | floppy_drive *drive = get_safe_token(device); |
| 910 | return drive->wpt; |
| 911 | } |
| 912 | |
| 913 | /* track 0 detect */ |
| 914 | READ_LINE_DEVICE_HANDLER( floppy_tk00_r ) |
| 915 | { |
| 916 | floppy_drive *drive = get_safe_token(device); |
| 917 | return drive->tk00; |
| 918 | } |
| 919 | |
| 920 | /* disk changed */ |
| 921 | READ_LINE_DEVICE_HANDLER( floppy_dskchg_r ) |
| 922 | { |
| 923 | floppy_drive *drive = get_safe_token(device); |
| 924 | return drive->dskchg; |
| 925 | } |
| 926 | |
| 927 | /* 2-sided disk */ |
| 928 | READ_LINE_DEVICE_HANDLER( floppy_twosid_r ) |
| 929 | { |
| 930 | floppy_drive *drive = get_safe_token(device); |
| 931 | |
| 932 | if (drive->floppy == NULL) |
| 933 | return ASSERT_LINE; |
| 934 | else |
| 935 | return !floppy_get_heads_per_disk(drive->floppy); |
| 936 | } |
| 937 | |
| 938 | /*------------------------------------------------- |
| 939 | DEVICE_IMAGE_SOFTLIST_LOAD(floppy) |
| 940 | -------------------------------------------------*/ |
| 941 | static DEVICE_IMAGE_SOFTLIST_LOAD(floppy) |
| 942 | { |
| 943 | return image.load_software(swlist, swname, start_entry); |
| 944 | } |
| 945 | |
| 946 | /************************************* |
| 947 | * |
| 948 | * Device specification function |
| 949 | * |
| 950 | *************************************/ |
| 951 | /*------------------------------------------------- |
| 952 | safe_strcpy - hack |
| 953 | -------------------------------------------------*/ |
| 954 | |
| 955 | static void safe_strcpy(char *dst, const char *src) |
| 956 | { |
| 957 | strcpy(dst, src ? src : ""); |
| 958 | } |
| 959 | |
| 960 | DEVICE_GET_INFO(floppy) |
| 961 | { |
| 962 | switch( state ) |
| 963 | { |
| 964 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 965 | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(floppy_drive); break; |
| 966 | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; |
| 967 | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_FLOPPY; break; |
| 968 | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 969 | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break; |
| 970 | case DEVINFO_INT_IMAGE_CREATABLE: |
| 971 | { |
| 972 | int cnt = 0; |
| 973 | if ( device && device->static_config() ) |
| 974 | { |
| 975 | const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats; |
| 976 | int i; |
| 977 | for ( i = 0; floppy_options[i].construct; i++ ) { |
| 978 | if(floppy_options[i].param_guidelines) cnt++; |
| 979 | } |
| 980 | } |
| 981 | info->i = (cnt>0) ? 1 : 0; |
| 982 | } |
| 983 | break; |
| 984 | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 985 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(floppy); break; |
| 986 | case DEVINFO_FCT_IMAGE_CREATE: info->f = (genf *) DEVICE_IMAGE_CREATE_NAME(floppy); break; |
| 987 | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(floppy); break; |
| 988 | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(floppy); break; |
| 989 | case DEVINFO_FCT_IMAGE_SOFTLIST_LOAD: info->f = (genf *) DEVICE_IMAGE_SOFTLIST_LOAD_NAME(floppy); break; |
| 990 | case DEVINFO_PTR_IMAGE_CREATE_OPTGUIDE: info->p = (void *)floppy_option_guide; break; |
| 991 | case DEVINFO_INT_IMAGE_CREATE_OPTCOUNT: |
| 992 | { |
| 993 | const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats; |
| 994 | int count; |
| 995 | for (count = 0; floppy_options[count].construct; count++) |
| 996 | ; |
| 997 | info->i = count; |
| 998 | break; |
| 999 | } |
| 1000 | |
| 1001 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 1002 | case DEVINFO_STR_NAME: strcpy(info->s, "Floppy Disk"); break; |
| 1003 | case DEVINFO_STR_FAMILY: strcpy(info->s, "Floppy Disk"); break; |
| 1004 | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 1005 | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: |
| 1006 | if ( device && device->static_config() ) |
| 1007 | { |
| 1008 | const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats; |
| 1009 | int i; |
| 1010 | /* set up a temporary string */ |
| 1011 | info->s[0] = '\0'; |
| 1012 | for ( i = 0; floppy_options[i].construct; i++ ) |
| 1013 | image_specify_extension( info->s, 256, floppy_options[i].extensions ); |
| 1014 | } |
| 1015 | break; |
| 1016 | case DEVINFO_STR_IMAGE_INTERFACE: |
| 1017 | if ( device && device->static_config() && ((floppy_config *)device->static_config())->interface) |
| 1018 | { |
| 1019 | strcpy(info->s, ((floppy_config *)device->static_config())->interface ); |
| 1020 | } |
| 1021 | break; |
| 1022 | default: |
| 1023 | { |
| 1024 | if ( device && device->static_config() ) |
| 1025 | { |
| 1026 | const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats; |
| 1027 | if ((state >= DEVINFO_PTR_IMAGE_CREATE_OPTSPEC) && (state < DEVINFO_PTR_IMAGE_CREATE_OPTSPEC + DEVINFO_IMAGE_CREATE_OPTMAX)) { |
| 1028 | info->p = (void *) floppy_options[state - DEVINFO_PTR_IMAGE_CREATE_OPTSPEC].param_guidelines; |
| 1029 | } else if ((state >= DEVINFO_STR_IMAGE_CREATE_OPTNAME) && (state < DEVINFO_STR_IMAGE_CREATE_OPTNAME + DEVINFO_IMAGE_CREATE_OPTMAX)) { |
| 1030 | safe_strcpy(info->s,floppy_options[state - DEVINFO_STR_IMAGE_CREATE_OPTNAME].name); |
| 1031 | } |
| 1032 | else if ((state >= DEVINFO_STR_IMAGE_CREATE_OPTDESC) && (state < DEVINFO_STR_IMAGE_CREATE_OPTDESC + DEVINFO_IMAGE_CREATE_OPTMAX)) { |
| 1033 | safe_strcpy(info->s,floppy_options[state - DEVINFO_STR_IMAGE_CREATE_OPTDESC].description); |
| 1034 | } |
| 1035 | else if ((state >= DEVINFO_STR_IMAGE_CREATE_OPTEXTS) && (state < DEVINFO_STR_IMAGE_CREATE_OPTEXTS + DEVINFO_IMAGE_CREATE_OPTMAX)) { |
| 1036 | safe_strcpy(info->s,floppy_options[state - DEVINFO_STR_IMAGE_CREATE_OPTEXTS].extensions); |
| 1037 | } |
| 1038 | } |
| 1039 | } |
| 1040 | |
| 1041 | break; |
| 1042 | } |
| 1043 | } |
| 1044 | |
| 1045 | DEFINE_LEGACY_IMAGE_DEVICE(FLOPPY, floppy); |
src/emu/imagedev/flopdrv.h
| r0 | r9928 | |
| 1 | /* flopdrv provides simple emulation of a disc drive */ |
| 2 | /* the 8271, upd765 and wd179x use this */ |
| 3 | |
| 4 | #ifndef __FLOPDRV_H__ |
| 5 | #define __FLOPDRV_H__ |
| 6 | |
| 7 | #include "devcb.h" |
| 8 | #include "image.h" |
| 9 | #include "flopimg.h" |
| 10 | |
| 11 | #define FLOPPY_TYPE_REGULAR 0 |
| 12 | #define FLOPPY_TYPE_APPLE 1 |
| 13 | #define FLOPPY_TYPE_SONY 2 |
| 14 | |
| 15 | #define FLOPPY_DRIVE_2_8_INCH 1 |
| 16 | #define FLOPPY_DRIVE_3_INCH 2 |
| 17 | #define FLOPPY_DRIVE_3_5_INCH 3 |
| 18 | #define FLOPPY_DRIVE_5_25_INCH 4 |
| 19 | #define FLOPPY_DRIVE_8_INCH 5 |
| 20 | |
| 21 | // Maximum supported density |
| 22 | #define FLOPPY_DRIVE_SD 1 |
| 23 | #define FLOPPY_DRIVE_DD 2 |
| 24 | #define FLOPPY_DRIVE_QD 3 |
| 25 | #define FLOPPY_DRIVE_HD 4 |
| 26 | #define FLOPPY_DRIVE_ED 5 |
| 27 | |
| 28 | #define FLOPPY_STANDARD_3_SSDD { FLOPPY_DRIVE_3_INCH, 1, 42, FLOPPY_DRIVE_DD } |
| 29 | #define FLOPPY_STANDARD_3_DSDD { FLOPPY_DRIVE_3_INCH, 2, 42, FLOPPY_DRIVE_DD } |
| 30 | #define FLOPPY_STANDARD_3_5_DSDD { FLOPPY_DRIVE_3_5_INCH, 2, 83, FLOPPY_DRIVE_DD } |
| 31 | #define FLOPPY_STANDARD_3_5_DSHD { FLOPPY_DRIVE_3_5_INCH, 2, 83, FLOPPY_DRIVE_HD } |
| 32 | #define FLOPPY_STANDARD_5_25_SSSD_35 { FLOPPY_DRIVE_5_25_INCH, 1, 37, FLOPPY_DRIVE_SD } |
| 33 | #define FLOPPY_STANDARD_5_25_DSSD_35 { FLOPPY_DRIVE_5_25_INCH, 2, 37, FLOPPY_DRIVE_SD } |
| 34 | #define FLOPPY_STANDARD_5_25_SSSD { FLOPPY_DRIVE_5_25_INCH, 1, 42, FLOPPY_DRIVE_SD } |
| 35 | #define FLOPPY_STANDARD_5_25_DSSD { FLOPPY_DRIVE_5_25_INCH, 2, 42, FLOPPY_DRIVE_SD } |
| 36 | #define FLOPPY_STANDARD_5_25_SSDD_40 { FLOPPY_DRIVE_5_25_INCH, 1, 42, FLOPPY_DRIVE_DD } |
| 37 | #define FLOPPY_STANDARD_5_25_DSDD_40 { FLOPPY_DRIVE_5_25_INCH, 2, 42, FLOPPY_DRIVE_DD } |
| 38 | #define FLOPPY_STANDARD_5_25_SSDD { FLOPPY_DRIVE_5_25_INCH, 1, 83, FLOPPY_DRIVE_DD } |
| 39 | #define FLOPPY_STANDARD_5_25_DSDD { FLOPPY_DRIVE_5_25_INCH, 2, 83, FLOPPY_DRIVE_DD } |
| 40 | #define FLOPPY_STANDARD_5_25_DSQD { FLOPPY_DRIVE_5_25_INCH, 2, 83, FLOPPY_DRIVE_QD } |
| 41 | #define FLOPPY_STANDARD_5_25_DSHD { FLOPPY_DRIVE_5_25_INCH, 2, 83, FLOPPY_DRIVE_HD } |
| 42 | #define FLOPPY_STANDARD_8_SSSD { FLOPPY_DRIVE_8_INCH, 1, 77, FLOPPY_DRIVE_SD } |
| 43 | #define FLOPPY_STANDARD_8_DSSD { FLOPPY_DRIVE_8_INCH, 2, 77, FLOPPY_DRIVE_SD } |
| 44 | #define FLOPPY_STANDARD_8_DSDD { FLOPPY_DRIVE_8_INCH, 2, 77, FLOPPY_DRIVE_DD } |
| 45 | |
| 46 | /*************************************************************************** |
| 47 | TYPE DEFINITIONS |
| 48 | ***************************************************************************/ |
| 49 | typedef struct floppy_type_t floppy_type; |
| 50 | struct floppy_type_t |
| 51 | { |
| 52 | UINT8 media_size; |
| 53 | UINT8 head_number; |
| 54 | UINT8 max_track_number; |
| 55 | UINT8 max_density; |
| 56 | }; |
| 57 | |
| 58 | typedef struct floppy_config_t floppy_config; |
| 59 | struct floppy_config_t |
| 60 | { |
| 61 | devcb_write_line out_idx_func; /* index */ |
| 62 | devcb_read_line in_mon_func; /* motor on */ |
| 63 | devcb_write_line out_tk00_func; /* track 00 */ |
| 64 | devcb_write_line out_wpt_func; /* write protect */ |
| 65 | devcb_write_line out_rdy_func; /* ready */ |
| 66 | // devcb_write_line out_dskchg_func; /* disk changed */ |
| 67 | |
| 68 | floppy_type_t floppy_type; |
| 69 | const struct FloppyFormat *formats; |
| 70 | const char *interface; |
| 71 | }; |
| 72 | |
| 73 | /* sector has a deleted data address mark */ |
| 74 | #define ID_FLAG_DELETED_DATA 0x0001 |
| 75 | /* CRC error in id field */ |
| 76 | #define ID_FLAG_CRC_ERROR_IN_ID_FIELD 0x0002 |
| 77 | /* CRC error in data field */ |
| 78 | #define ID_FLAG_CRC_ERROR_IN_DATA_FIELD 0x0004 |
| 79 | |
| 80 | typedef struct chrn_id |
| 81 | { |
| 82 | unsigned char C; |
| 83 | unsigned char H; |
| 84 | unsigned char R; |
| 85 | unsigned char N; |
| 86 | int data_id; // id for read/write data command |
| 87 | unsigned long flags; |
| 88 | } chrn_id; |
| 89 | |
| 90 | /* set if drive is ready */ |
| 91 | #define FLOPPY_DRIVE_READY 0x0010 |
| 92 | /* set if index has just occured */ |
| 93 | #define FLOPPY_DRIVE_INDEX 0x0020 |
| 94 | |
| 95 | /* a callback which will be executed if the ready state of the drive changes e.g. not ready->ready, ready->not ready */ |
| 96 | void floppy_drive_set_ready_state_change_callback(device_t *img, void (*callback)(device_t *controller,device_t *img, int state)); |
| 97 | |
| 98 | void floppy_drive_set_index_pulse_callback(device_t *img, void (*callback)(device_t *controller,device_t *image, int state)); |
| 99 | |
| 100 | /* set flag state */ |
| 101 | int floppy_drive_get_flag_state(device_t *img, int flag); |
| 102 | /* get flag state */ |
| 103 | void floppy_drive_set_flag_state(device_t *img, int flag, int state); |
| 104 | /* get current physical track drive is on */ |
| 105 | int floppy_drive_get_current_track(device_t *img); |
| 106 | /* get current physical track size */ |
| 107 | UINT64 floppy_drive_get_current_track_size(device_t *img, int head); |
| 108 | |
| 109 | /* get next id from track, 1 if got a id, 0 if no id was got */ |
| 110 | int floppy_drive_get_next_id(device_t *img, int side, chrn_id *); |
| 111 | /* set ready state of drive. If flag == 1, set ready state only if drive present, |
| 112 | disk is in drive, and motor is on. Otherwise set ready state to the state passed */ |
| 113 | void floppy_drive_set_ready_state(device_t *img, int state, int flag); |
| 114 | |
| 115 | /* seek up or down */ |
| 116 | void floppy_drive_seek(device_t *img, signed int signed_tracks); |
| 117 | |
| 118 | void floppy_drive_read_track_data_info_buffer(device_t *img, int side, void *ptr, int *length ); |
| 119 | void floppy_drive_write_track_data_info_buffer(device_t *img, int side, const void *ptr, int *length ); |
| 120 | void floppy_drive_format_sector(device_t *img, int side, int sector_index, int c, int h, int r, int n, int filler); |
| 121 | void floppy_drive_read_sector_data(device_t *img, int side, int index1, void *pBuffer, int length); |
| 122 | void floppy_drive_write_sector_data(device_t *img, int side, int index1, const void *pBuffer, int length, int ddam); |
| 123 | |
| 124 | /* set motor speed to get correct index pulses |
| 125 | standard RPM are 300 RPM (common) and 360 RPM |
| 126 | Note: this actually only works for soft sectored disks: one index pulse per |
| 127 | track. |
| 128 | */ |
| 129 | void floppy_drive_set_rpm(device_t *image, float rpm); |
| 130 | |
| 131 | void floppy_drive_set_controller(device_t *img, device_t *controller); |
| 132 | |
| 133 | floppy_image *flopimg_get_image(device_t *image); |
| 134 | |
| 135 | /* hack for apple II; replace this when we think of something better */ |
| 136 | void floppy_install_unload_proc(device_t *image, void (*proc)(device_image_interface &image)); |
| 137 | |
| 138 | void floppy_install_load_proc(device_t *image, void (*proc)(device_image_interface &image)); |
| 139 | |
| 140 | device_t *floppy_get_device(running_machine *machine,int drive); |
| 141 | device_t *floppy_get_device_by_type(running_machine *machine,int ftype,int drive); |
| 142 | int floppy_get_drive_type(device_t *image); |
| 143 | void floppy_set_type(device_t *image,int ftype); |
| 144 | int floppy_get_count(running_machine *machine); |
| 145 | |
| 146 | int floppy_get_drive(device_t *image); |
| 147 | int floppy_get_drive_by_type(device_t *image,int ftype); |
| 148 | |
| 149 | void *flopimg_get_custom_data(device_t *image); |
| 150 | void flopimg_alloc_custom_data(device_t *image,void *custom); |
| 151 | |
| 152 | void floppy_drive_set_geometry(device_t *img, floppy_type_t type); |
| 153 | |
| 154 | /* drive select lines */ |
| 155 | WRITE_LINE_DEVICE_HANDLER( floppy_ds0_w ); |
| 156 | WRITE_LINE_DEVICE_HANDLER( floppy_ds1_w ); |
| 157 | WRITE_LINE_DEVICE_HANDLER( floppy_ds2_w ); |
| 158 | WRITE_LINE_DEVICE_HANDLER( floppy_ds3_w ); |
| 159 | WRITE8_DEVICE_HANDLER( floppy_ds_w ); |
| 160 | |
| 161 | WRITE_LINE_DEVICE_HANDLER( floppy_mon_w ); |
| 162 | WRITE_LINE_DEVICE_HANDLER( floppy_drtn_w ); |
| 163 | WRITE_LINE_DEVICE_HANDLER( floppy_stp_w ); |
| 164 | WRITE_LINE_DEVICE_HANDLER( floppy_wtd_w ); |
| 165 | WRITE_LINE_DEVICE_HANDLER( floppy_wtg_w ); |
| 166 | |
| 167 | /* write-protect */ |
| 168 | READ_LINE_DEVICE_HANDLER( floppy_wpt_r ); |
| 169 | |
| 170 | /* track 0 detect */ |
| 171 | READ_LINE_DEVICE_HANDLER( floppy_tk00_r ); |
| 172 | |
| 173 | /* disk changed */ |
| 174 | READ_LINE_DEVICE_HANDLER( floppy_dskchg_r ); |
| 175 | |
| 176 | /* 2-sided disk */ |
| 177 | READ_LINE_DEVICE_HANDLER( floppy_twosid_r ); |
| 178 | |
| 179 | DECLARE_LEGACY_IMAGE_DEVICE(FLOPPY, floppy); |
| 180 | |
| 181 | extern DEVICE_START( floppy ); |
| 182 | extern DEVICE_IMAGE_LOAD( floppy ); |
| 183 | extern DEVICE_IMAGE_CREATE( floppy ); |
| 184 | extern DEVICE_IMAGE_UNLOAD( floppy ); |
| 185 | |
| 186 | /*************************************************************************** |
| 187 | DEVICE CONFIGURATION MACROS |
| 188 | ***************************************************************************/ |
| 189 | #define FLOPPY_0 "floppy0" |
| 190 | #define FLOPPY_1 "floppy1" |
| 191 | #define FLOPPY_2 "floppy2" |
| 192 | #define FLOPPY_3 "floppy3" |
| 193 | |
| 194 | |
| 195 | #define MCFG_FLOPPY_DRIVE_ADD(_tag, _config) \ |
| 196 | MCFG_DEVICE_ADD(_tag, FLOPPY, 0) \ |
| 197 | MCFG_DEVICE_CONFIG(_config) |
| 198 | |
| 199 | #define MCFG_FLOPPY_DRIVE_MODIFY(_tag, _config) \ |
| 200 | MCFG_DEVICE_MODIFY(_tag) \ |
| 201 | MCFG_DEVICE_CONFIG(_config) |
| 202 | |
| 203 | #define MCFG_FLOPPY_4_DRIVES_ADD(_config) \ |
| 204 | MCFG_DEVICE_ADD(FLOPPY_0, FLOPPY, 0) \ |
| 205 | MCFG_DEVICE_CONFIG(_config) \ |
| 206 | MCFG_DEVICE_ADD(FLOPPY_1, FLOPPY, 0) \ |
| 207 | MCFG_DEVICE_CONFIG(_config) \ |
| 208 | MCFG_DEVICE_ADD(FLOPPY_2, FLOPPY, 0) \ |
| 209 | MCFG_DEVICE_CONFIG(_config) \ |
| 210 | MCFG_DEVICE_ADD(FLOPPY_3, FLOPPY, 0) \ |
| 211 | MCFG_DEVICE_CONFIG(_config) |
| 212 | |
| 213 | #define MCFG_FLOPPY_4_DRIVES_MODIFY(_config) \ |
| 214 | MCFG_DEVICE_MODIFY(FLOPPY_0) \ |
| 215 | MCFG_DEVICE_CONFIG(_config) \ |
| 216 | MCFG_DEVICE_MODIFY(FLOPPY_1) \ |
| 217 | MCFG_DEVICE_CONFIG(_config) \ |
| 218 | MCFG_DEVICE_MODIFY(FLOPPY_2) \ |
| 219 | MCFG_DEVICE_CONFIG(_config) \ |
| 220 | MCFG_DEVICE_MODIFY(FLOPPY_3) \ |
| 221 | MCFG_DEVICE_CONFIG(_config) |
| 222 | |
| 223 | #define MCFG_FLOPPY_4_DRIVES_REMOVE() \ |
| 224 | MCFG_DEVICE_REMOVE(FLOPPY_0) \ |
| 225 | MCFG_DEVICE_REMOVE(FLOPPY_1) \ |
| 226 | MCFG_DEVICE_REMOVE(FLOPPY_2) \ |
| 227 | MCFG_DEVICE_REMOVE(FLOPPY_3) |
| 228 | |
| 229 | #define MCFG_FLOPPY_2_DRIVES_ADD(_config) \ |
| 230 | MCFG_DEVICE_ADD(FLOPPY_0, FLOPPY, 0) \ |
| 231 | MCFG_DEVICE_CONFIG(_config) \ |
| 232 | MCFG_DEVICE_ADD(FLOPPY_1, FLOPPY, 0) \ |
| 233 | MCFG_DEVICE_CONFIG(_config) |
| 234 | |
| 235 | #define MCFG_FLOPPY_2_DRIVES_MODIFY(_config) \ |
| 236 | MCFG_DEVICE_MODIFY(FLOPPY_0) \ |
| 237 | MCFG_DEVICE_CONFIG(_config) \ |
| 238 | MCFG_DEVICE_MODIFY(FLOPPY_1) \ |
| 239 | MCFG_DEVICE_CONFIG(_config) |
| 240 | |
| 241 | #define MCFG_FLOPPY_2_DRIVES_REMOVE() \ |
| 242 | MCFG_DEVICE_REMOVE(FLOPPY_0) \ |
| 243 | MCFG_DEVICE_REMOVE(FLOPPY_1) |
| 244 | |
| 245 | #endif /* __FLOPDRV_H__ */ |
src/emu/imagedev/cassimg.c
| r0 | r9928 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | cassimg.c |
| 4 | |
| 5 | Cassette tape image abstraction code |
| 6 | |
| 7 | *********************************************************************/ |
| 8 | |
| 9 | |
| 10 | #include <string.h> |
| 11 | |
| 12 | #include "emu.h" |
| 13 | #include "imageutl.h" |
| 14 | #include "pool.h" |
| 15 | #include "cassimg.h" |
| 16 | |
| 17 | |
| 18 | /* debugging parameters */ |
| 19 | #define LOG_PUT_SAMPLES 0 |
| 20 | #define DUMP_CASSETTES 0 |
| 21 | |
| 22 | #define SAMPLES_PER_BLOCK 0x40000 |
| 23 | #define CASSETTE_FLAG_DIRTY 0x10000 |
| 24 | |
| 25 | |
| 26 | CASSETTE_FORMATLIST_START(cassette_default_formats) |
| 27 | CASSETTE_FORMATLIST_END |
| 28 | |
| 29 | |
| 30 | |
| 31 | /********************************************************************* |
| 32 | helper code |
| 33 | *********************************************************************/ |
| 34 | |
| 35 | static double map_double(double d, UINT64 low, UINT64 high, UINT64 value) |
| 36 | { |
| 37 | #if defined(_MSC_VER) && (_MSC_VER <= 1200) |
| 38 | /* casting unsigned __int64 to double is not supported on VC6 or before */ |
| 39 | return d * (INT64)(value - low) / (INT64)(high - low); |
| 40 | #else |
| 41 | return d * (value - low) / (high - low); |
| 42 | #endif |
| 43 | } |
| 44 | |
| 45 | |
| 46 | |
| 47 | static size_t waveform_bytes_per_sample(int waveform_flags) |
| 48 | { |
| 49 | return (size_t) (1 << ((waveform_flags & 0x06) / 2)); |
| 50 | } |
| 51 | |
| 52 | |
| 53 | |
| 54 | /********************************************************************* |
| 55 | extrapolation and interpolation |
| 56 | *********************************************************************/ |
| 57 | |
| 58 | static INT32 extrapolate8(INT8 value) |
| 59 | { |
| 60 | return ((INT32) value) << 24; |
| 61 | } |
| 62 | |
| 63 | static INT32 extrapolate16(INT16 value) |
| 64 | { |
| 65 | return ((INT32) value) << 16; |
| 66 | } |
| 67 | |
| 68 | static INT8 interpolate8(INT32 value) |
| 69 | { |
| 70 | return (INT8) (value >> 24); |
| 71 | } |
| 72 | |
| 73 | static INT16 interpolate16(INT32 value) |
| 74 | { |
| 75 | return (INT16) (value >> 16); |
| 76 | } |
| 77 | |
| 78 | |
| 79 | |
| 80 | /********************************************************************* |
| 81 | initialization and termination |
| 82 | *********************************************************************/ |
| 83 | |
| 84 | static cassette_image *cassette_init(running_machine *machine,const struct CassetteFormat *format, void *file, const struct io_procs *procs, int flags) |
| 85 | { |
| 86 | cassette_image *cassette; |
| 87 | |
| 88 | cassette = (cassette_image *)malloc(sizeof(struct _cassette_image)); |
| 89 | if (!cassette) |
| 90 | return NULL; |
| 91 | |
| 92 | memset(cassette, 0, sizeof(*cassette)); |
| 93 | cassette->format = format; |
| 94 | cassette->io.file = file; |
| 95 | cassette->io.procs = procs; |
| 96 | cassette->flags = flags; |
| 97 | cassette->pool = pool_alloc_lib(NULL); |
| 98 | cassette->machine = machine; |
| 99 | return cassette; |
| 100 | } |
| 101 | |
| 102 | |
| 103 | |
| 104 | static void cassette_finishinit(casserr_t err, cassette_image *cassette, cassette_image **outcassette) |
| 105 | { |
| 106 | if (cassette && (err || !outcassette)) |
| 107 | { |
| 108 | cassette_close(cassette); |
| 109 | cassette = NULL; |
| 110 | } |
| 111 | if (outcassette) |
| 112 | *outcassette = cassette; |
| 113 | } |
| 114 | |
| 115 | |
| 116 | |
| 117 | static int good_format(const struct CassetteFormat *format, const char *extension, int flags) |
| 118 | { |
| 119 | if (extension && !image_find_extension(format->extensions, extension)) |
| 120 | return FALSE; |
| 121 | if (((flags & CASSETTE_FLAG_READONLY) == 0) && !format->save) |
| 122 | return FALSE; |
| 123 | return TRUE; |
| 124 | } |
| 125 | |
| 126 | |
| 127 | |
| 128 | casserr_t cassette_open_choices(running_machine *machine,void *file, const struct io_procs *procs, const char *extension, |
| 129 | const struct CassetteFormat *const *formats, int flags, cassette_image **outcassette) |
| 130 | { |
| 131 | casserr_t err; |
| 132 | cassette_image *cassette; |
| 133 | const struct CassetteFormat *format; |
| 134 | struct CassetteOptions opts = {0, }; |
| 135 | int i; |
| 136 | |
| 137 | /* if not specified, use the dummy arguments */ |
| 138 | if (!formats) |
| 139 | formats = cassette_default_formats; |
| 140 | |
| 141 | /* create the cassette object */ |
| 142 | cassette = cassette_init(machine, NULL, file, procs, flags); |
| 143 | if (!cassette) |
| 144 | { |
| 145 | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 146 | goto done; |
| 147 | } |
| 148 | |
| 149 | /* identify the image */ |
| 150 | format = NULL; |
| 151 | for (i = 0; !format && formats[i]; i++) |
| 152 | { |
| 153 | if (good_format(formats[i], extension, flags)) |
| 154 | { |
| 155 | format = formats[i]; |
| 156 | memset(&opts, 0, sizeof(opts)); |
| 157 | err = format->identify(cassette, &opts); |
| 158 | if (err == CASSETTE_ERROR_INVALIDIMAGE) |
| 159 | format = NULL; |
| 160 | else if (err) |
| 161 | goto done; |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | /* have we found a proper format */ |
| 166 | if (!format) |
| 167 | { |
| 168 | err = CASSETTE_ERROR_INVALIDIMAGE; |
| 169 | goto done; |
| 170 | } |
| 171 | cassette->format = format; |
| 172 | |
| 173 | /* read the options */ |
| 174 | cassette->channels = opts.channels; |
| 175 | cassette->sample_frequency = opts.sample_frequency; |
| 176 | |
| 177 | /* load the image */ |
| 178 | err = format->load(cassette); |
| 179 | if (err) |
| 180 | goto done; |
| 181 | |
| 182 | /* success */ |
| 183 | cassette->flags &= ~CASSETTE_FLAG_DIRTY; |
| 184 | err = CASSETTE_ERROR_SUCCESS; |
| 185 | |
| 186 | done: |
| 187 | cassette_finishinit(err, cassette, outcassette); |
| 188 | return err; |
| 189 | } |
| 190 | |
| 191 | |
| 192 | |
| 193 | casserr_t cassette_open(running_machine *machine, void *file, const struct io_procs *procs, |
| 194 | const struct CassetteFormat *format, int flags, cassette_image **outcassette) |
| 195 | { |
| 196 | const struct CassetteFormat *formats[2]; |
| 197 | formats[0] = format; |
| 198 | formats[1] = NULL; |
| 199 | return cassette_open_choices(machine, file, procs, NULL, formats, flags, outcassette); |
| 200 | } |
| 201 | |
| 202 | |
| 203 | |
| 204 | casserr_t cassette_create(running_machine *machine, void *file, const struct io_procs *procs, const struct CassetteFormat *format, |
| 205 | const struct CassetteOptions *opts, int flags, cassette_image **outcassette) |
| 206 | { |
| 207 | casserr_t err; |
| 208 | cassette_image *cassette; |
| 209 | static const struct CassetteOptions default_options = { 1, 16, 44100 }; |
| 210 | |
| 211 | /* cannot create to a read only image */ |
| 212 | if (flags & CASSETTE_FLAG_READONLY) |
| 213 | return CASSETTE_ERROR_INVALIDIMAGE; |
| 214 | |
| 215 | /* is this a good format? */ |
| 216 | if (!good_format(format, NULL, flags)) |
| 217 | return CASSETTE_ERROR_INVALIDIMAGE; |
| 218 | |
| 219 | /* normalize arguments */ |
| 220 | if (!opts) |
| 221 | opts = &default_options; |
| 222 | |
| 223 | /* create the cassette object */ |
| 224 | cassette = cassette_init(machine, format, file, procs, flags); |
| 225 | if (!cassette) |
| 226 | { |
| 227 | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 228 | goto done; |
| 229 | } |
| 230 | |
| 231 | /* read the options */ |
| 232 | cassette->channels = opts->channels; |
| 233 | cassette->sample_frequency = opts->sample_frequency; |
| 234 | |
| 235 | err = CASSETTE_ERROR_SUCCESS; |
| 236 | |
| 237 | done: |
| 238 | cassette_finishinit(err, cassette, outcassette); |
| 239 | return err; |
| 240 | } |
| 241 | |
| 242 | |
| 243 | |
| 244 | static casserr_t cassette_perform_save(cassette_image *cassette) |
| 245 | { |
| 246 | struct CassetteInfo info; |
| 247 | cassette_get_info(cassette, &info); |
| 248 | return cassette->format->save(cassette, &info); |
| 249 | } |
| 250 | |
| 251 | |
| 252 | |
| 253 | casserr_t cassette_save(cassette_image *cassette) |
| 254 | { |
| 255 | casserr_t err; |
| 256 | |
| 257 | if (!cassette->format || !cassette->format->save) |
| 258 | return CASSETTE_ERROR_UNSUPPORTED; |
| 259 | |
| 260 | err = cassette_perform_save(cassette); |
| 261 | if (err) |
| 262 | return err; |
| 263 | |
| 264 | cassette->flags &= ~CASSETTE_FLAG_DIRTY; |
| 265 | return CASSETTE_ERROR_SUCCESS; |
| 266 | } |
| 267 | |
| 268 | |
| 269 | |
| 270 | void cassette_get_info(cassette_image *cassette, struct CassetteInfo *info) |
| 271 | { |
| 272 | memset(info, 0, sizeof(*info)); |
| 273 | info->channels = cassette->channels; |
| 274 | info->sample_count = cassette->sample_count; |
| 275 | info->sample_frequency = cassette->sample_frequency; |
| 276 | info->bits_per_sample = (int) waveform_bytes_per_sample(cassette->flags) * 8; |
| 277 | } |
| 278 | |
| 279 | |
| 280 | |
| 281 | void cassette_close(cassette_image *cassette) |
| 282 | { |
| 283 | if (cassette) |
| 284 | { |
| 285 | if ((cassette->flags & CASSETTE_FLAG_DIRTY) && (cassette->flags & CASSETTE_FLAG_SAVEONEXIT)) |
| 286 | cassette_save(cassette); |
| 287 | pool_free_lib(cassette->pool); |
| 288 | free(cassette); |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | |
| 293 | |
| 294 | void cassette_change(cassette_image *cassette, void *file, const struct io_procs *procs, const struct CassetteFormat *format, int flags) |
| 295 | { |
| 296 | if ((flags & CASSETTE_FLAG_READONLY) == 0) |
| 297 | flags |= CASSETTE_FLAG_DIRTY; |
| 298 | cassette->io.file = file; |
| 299 | cassette->io.procs = procs; |
| 300 | cassette->format = format; |
| 301 | cassette->flags = flags; |
| 302 | } |
| 303 | |
| 304 | |
| 305 | |
| 306 | /********************************************************************* |
| 307 | calls for accessing the raw cassette image |
| 308 | *********************************************************************/ |
| 309 | |
| 310 | void cassette_image_read(cassette_image *cassette, void *buffer, UINT64 offset, size_t length) |
| 311 | { |
| 312 | io_generic_read(&cassette->io, buffer, offset, length); |
| 313 | } |
| 314 | |
| 315 | |
| 316 | |
| 317 | void cassette_image_write(cassette_image *cassette, const void *buffer, UINT64 offset, size_t length) |
| 318 | { |
| 319 | io_generic_write(&cassette->io, buffer, offset, length); |
| 320 | } |
| 321 | |
| 322 | |
| 323 | |
| 324 | UINT64 cassette_image_size(cassette_image *cassette) |
| 325 | { |
| 326 | return io_generic_size(&cassette->io); |
| 327 | } |
| 328 | |
| 329 | |
| 330 | |
| 331 | /********************************************************************* |
| 332 | waveform accesses |
| 333 | *********************************************************************/ |
| 334 | |
| 335 | struct manipulation_ranges |
| 336 | { |
| 337 | int channel_first; |
| 338 | int channel_last; |
| 339 | size_t sample_first; |
| 340 | size_t sample_last; |
| 341 | }; |
| 342 | |
| 343 | |
| 344 | |
| 345 | static size_t my_round(double d) |
| 346 | { |
| 347 | size_t result; |
| 348 | d += 0.5; |
| 349 | result = (size_t) d; |
| 350 | return result; |
| 351 | } |
| 352 | |
| 353 | |
| 354 | |
| 355 | static casserr_t compute_manipulation_ranges(cassette_image *cassette, int channel, |
| 356 | double time_index, double sample_period, struct manipulation_ranges *ranges) |
| 357 | { |
| 358 | if (channel < 0) |
| 359 | { |
| 360 | ranges->channel_first = 0; |
| 361 | ranges->channel_last = cassette->channels - 1; |
| 362 | } |
| 363 | else |
| 364 | { |
| 365 | ranges->channel_first = channel; |
| 366 | ranges->channel_last = channel; |
| 367 | } |
| 368 | |
| 369 | ranges->sample_first = my_round(time_index * cassette->sample_frequency); |
| 370 | ranges->sample_last = my_round((time_index + sample_period) * cassette->sample_frequency); |
| 371 | |
| 372 | if (ranges->sample_last > ranges->sample_first) |
| 373 | ranges->sample_last--; |
| 374 | |
| 375 | return CASSETTE_ERROR_SUCCESS; |
| 376 | } |
| 377 | |
| 378 | |
| 379 | |
| 380 | static casserr_t lookup_sample(cassette_image *cassette, int channel, size_t sample, int allocate, INT32 **ptr) |
| 381 | { |
| 382 | size_t sample_block; |
| 383 | size_t sample_index; |
| 384 | size_t sample_size; |
| 385 | size_t new_block_count; |
| 386 | size_t new_block_sample_count; |
| 387 | INT32 *new_block; |
| 388 | struct sample_block *new_blocks; |
| 389 | struct sample_block *block; |
| 390 | |
| 391 | *ptr = NULL; |
| 392 | sample_block = (sample / SAMPLES_PER_BLOCK) * cassette->channels + channel; |
| 393 | sample_index = sample % SAMPLES_PER_BLOCK; |
| 394 | sample_size = sizeof(block->block[0]); |
| 395 | |
| 396 | /* is this block beyond the edge of our waveform? */ |
| 397 | if (sample_block >= cassette->block_count) |
| 398 | { |
| 399 | if (!allocate) |
| 400 | return CASSETTE_ERROR_SUCCESS; |
| 401 | |
| 402 | /* allocate new blocks */ |
| 403 | new_block_count = sample_block + 1; |
| 404 | new_blocks = (struct sample_block *)pool_realloc_lib(cassette->pool, cassette->blocks, new_block_count * sizeof(cassette->blocks[0])); |
| 405 | if (!new_blocks) |
| 406 | return CASSETTE_ERROR_OUTOFMEMORY; |
| 407 | |
| 408 | cassette->blocks = new_blocks; |
| 409 | memset(&cassette->blocks[cassette->block_count], 0, (new_block_count - cassette->block_count) * sizeof(cassette->blocks[0])); |
| 410 | cassette->block_count = new_block_count; |
| 411 | } |
| 412 | |
| 413 | block = &cassette->blocks[sample_block]; |
| 414 | |
| 415 | /* is this sample access off the current block? */ |
| 416 | if (sample_index >= block->sample_count) |
| 417 | { |
| 418 | if (!allocate) |
| 419 | return CASSETTE_ERROR_SUCCESS; |
| 420 | |
| 421 | new_block_sample_count = SAMPLES_PER_BLOCK; |
| 422 | |
| 423 | new_block = (INT32*)pool_realloc_lib(cassette->pool, block->block, new_block_sample_count * sample_size); |
| 424 | if (!new_block) |
| 425 | return CASSETTE_ERROR_OUTOFMEMORY; |
| 426 | |
| 427 | block->block = new_block; |
| 428 | memset(&block->block[block->sample_count], 0, (new_block_sample_count - block->sample_count) * sample_size); |
| 429 | block->sample_count = new_block_sample_count; |
| 430 | } |
| 431 | |
| 432 | *ptr = &block->block[sample_index]; |
| 433 | return CASSETTE_ERROR_SUCCESS; |
| 434 | } |
| 435 | |
| 436 | |
| 437 | |
| 438 | /********************************************************************* |
| 439 | waveform accesses |
| 440 | *********************************************************************/ |
| 441 | |
| 442 | casserr_t cassette_get_samples(cassette_image *cassette, int channel, |
| 443 | double time_index, double sample_period, size_t sample_count, size_t sample_bytes, |
| 444 | void *samples, int waveform_flags) |
| 445 | { |
| 446 | casserr_t err; |
| 447 | struct manipulation_ranges ranges; |
| 448 | size_t sample_index; |
| 449 | size_t cassette_sample_index; |
| 450 | UINT8 *dest_ptr; |
| 451 | const INT32 *source_ptr; |
| 452 | double d; |
| 453 | INT16 word; |
| 454 | INT32 dword; |
| 455 | INT64 sum; |
| 456 | |
| 457 | assert(cassette); |
| 458 | |
| 459 | err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges); |
| 460 | if (err) |
| 461 | return err; |
| 462 | |
| 463 | for (sample_index = 0; sample_index < sample_count; sample_index++) |
| 464 | { |
| 465 | sum = 0; |
| 466 | |
| 467 | for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++) |
| 468 | { |
| 469 | /* find the sample that we are putting */ |
| 470 | d = map_double(ranges.sample_last + 1 - ranges.sample_first, 0, sample_count, sample_index) + ranges.sample_first; |
| 471 | cassette_sample_index = (size_t) d; |
| 472 | err = lookup_sample(cassette, channel, cassette_sample_index, TRUE, (INT32 **) &source_ptr); |
| 473 | if (err) |
| 474 | return err; |
| 475 | |
| 476 | sum += *source_ptr; |
| 477 | } |
| 478 | |
| 479 | /* average out the samples */ |
| 480 | sum /= (ranges.channel_last + 1 - ranges.channel_first); |
| 481 | |
| 482 | /* and write out the result */ |
| 483 | dest_ptr = (UINT8*)samples; |
| 484 | dest_ptr += waveform_bytes_per_sample(waveform_flags) * sample_index; |
| 485 | switch(waveform_bytes_per_sample(waveform_flags)) |
| 486 | { |
| 487 | case 1: |
| 488 | *((INT8 *) dest_ptr) = interpolate8(sum); |
| 489 | break; |
| 490 | case 2: |
| 491 | word = interpolate16(sum); |
| 492 | if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) |
| 493 | word = FLIPENDIAN_INT16(word); |
| 494 | *((INT16 *) dest_ptr) = word; |
| 495 | break; |
| 496 | case 4: |
| 497 | dword = sum; |
| 498 | if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) |
| 499 | dword = FLIPENDIAN_INT32(dword); |
| 500 | *((INT32 *) dest_ptr) = dword; |
| 501 | break; |
| 502 | } |
| 503 | } |
| 504 | return CASSETTE_ERROR_SUCCESS; |
| 505 | } |
| 506 | |
| 507 | |
| 508 | |
| 509 | casserr_t cassette_put_samples(cassette_image *cassette, int channel, |
| 510 | double time_index, double sample_period, size_t sample_count, size_t sample_bytes, |
| 511 | const void *samples, int waveform_flags) |
| 512 | { |
| 513 | casserr_t err; |
| 514 | struct manipulation_ranges ranges; |
| 515 | size_t sample_index; |
| 516 | INT32 *dest_ptr; |
| 517 | INT32 dest_value; |
| 518 | INT16 word; |
| 519 | INT32 dword; |
| 520 | const UINT8 *source_ptr; |
| 521 | double d; |
| 522 | |
| 523 | if (!cassette) |
| 524 | return CASSETTE_ERROR_SUCCESS; |
| 525 | |
| 526 | if (sample_period == 0) |
| 527 | return CASSETTE_ERROR_SUCCESS; |
| 528 | |
| 529 | err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges); |
| 530 | if (err) |
| 531 | return err; |
| 532 | |
| 533 | if (cassette->sample_count < ranges.sample_last+1) |
| 534 | cassette->sample_count = ranges.sample_last + 1; |
| 535 | cassette->flags |= CASSETTE_FLAG_DIRTY; |
| 536 | |
| 537 | if (LOG_PUT_SAMPLES) |
| 538 | { |
| 539 | logerror("cassette_put_samples(): Putting samples TIME=[%2.6g..%2.6g] INDEX=[%i..%i]\n", |
| 540 | time_index, time_index + sample_period, |
| 541 | (int)ranges.sample_first, (int)ranges.sample_last); |
| 542 | } |
| 543 | |
| 544 | for (sample_index = ranges.sample_first; sample_index <= ranges.sample_last; sample_index++) |
| 545 | { |
| 546 | /* figure out the source pointer */ |
| 547 | d = map_double(sample_count, ranges.sample_first, ranges.sample_last + 1, sample_index); |
| 548 | source_ptr = (const UINT8*)samples; |
| 549 | source_ptr += ((size_t) d) * sample_bytes; |
| 550 | |
| 551 | /* compute the value that we are writing */ |
| 552 | switch(waveform_bytes_per_sample(waveform_flags)) { |
| 553 | case 1: |
| 554 | dest_value = extrapolate8(*((INT8 *) source_ptr)); |
| 555 | break; |
| 556 | case 2: |
| 557 | word = *((INT16 *) source_ptr); |
| 558 | if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) |
| 559 | word = FLIPENDIAN_INT16(word); |
| 560 | dest_value = extrapolate16(word); |
| 561 | break; |
| 562 | case 4: |
| 563 | dword = *((INT32 *) source_ptr); |
| 564 | if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) |
| 565 | dword = FLIPENDIAN_INT32(dword); |
| 566 | dest_value = dword; |
| 567 | break; |
| 568 | default: |
| 569 | return CASSETTE_ERROR_INTERNAL; |
| 570 | } |
| 571 | |
| 572 | for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++) |
| 573 | { |
| 574 | /* find the sample that we are putting */ |
| 575 | err = lookup_sample(cassette, channel, sample_index, TRUE, &dest_ptr); |
| 576 | if (err) |
| 577 | return err; |
| 578 | *dest_ptr = dest_value; |
| 579 | } |
| 580 | } |
| 581 | return CASSETTE_ERROR_SUCCESS; |
| 582 | } |
| 583 | |
| 584 | |
| 585 | |
| 586 | casserr_t cassette_get_sample(cassette_image *cassette, int channel, |
| 587 | double time_index, double sample_period, INT32 *sample) |
| 588 | { |
| 589 | return cassette_get_samples(cassette, channel, time_index, |
| 590 | sample_period, 1, 0, sample, CASSETTE_WAVEFORM_32BIT); |
| 591 | } |
| 592 | |
| 593 | |
| 594 | |
| 595 | casserr_t cassette_put_sample(cassette_image *cassette, int channel, |
| 596 | double time_index, double sample_period, INT32 sample) |
| 597 | { |
| 598 | return cassette_put_samples(cassette, channel, time_index, |
| 599 | sample_period, 1, 0, &sample, CASSETTE_WAVEFORM_32BIT); |
| 600 | } |
| 601 | |
| 602 | |
| 603 | |
| 604 | /********************************************************************* |
| 605 | waveform accesses to/from the raw image |
| 606 | *********************************************************************/ |
| 607 | |
| 608 | casserr_t cassette_read_samples(cassette_image *cassette, int channels, double time_index, |
| 609 | double sample_period, size_t sample_count, UINT64 offset, int waveform_flags) |
| 610 | { |
| 611 | casserr_t err; |
| 612 | size_t chunk_sample_count; |
| 613 | size_t bytes_per_sample; |
| 614 | size_t sample_bytes; |
| 615 | size_t samples_loaded = 0; |
| 616 | double chunk_time_index; |
| 617 | double chunk_sample_period; |
| 618 | int channel; |
| 619 | UINT8 buffer[8192]; |
| 620 | |
| 621 | bytes_per_sample = waveform_bytes_per_sample(waveform_flags); |
| 622 | sample_bytes = bytes_per_sample * channels; |
| 623 | |
| 624 | while(samples_loaded < sample_count) |
| 625 | { |
| 626 | chunk_sample_count = MIN(sizeof(buffer) / sample_bytes, (sample_count - samples_loaded)); |
| 627 | chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count); |
| 628 | chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_loaded); |
| 629 | |
| 630 | cassette_image_read(cassette, buffer, offset, chunk_sample_count * sample_bytes); |
| 631 | |
| 632 | for (channel = 0; channel < channels; channel++) |
| 633 | { |
| 634 | err = cassette_put_samples(cassette, channel, chunk_time_index, chunk_sample_period, |
| 635 | chunk_sample_count, sample_bytes, &buffer[channel * bytes_per_sample], waveform_flags); |
| 636 | if (err) |
| 637 | return err; |
| 638 | } |
| 639 | |
| 640 | offset += chunk_sample_count * sample_bytes; |
| 641 | samples_loaded += chunk_sample_count; |
| 642 | } |
| 643 | return CASSETTE_ERROR_SUCCESS; |
| 644 | } |
| 645 | |
| 646 | |
| 647 | |
| 648 | casserr_t cassette_write_samples(cassette_image *cassette, int channels, double time_index, |
| 649 | double sample_period, size_t sample_count, UINT64 offset, int waveform_flags) |
| 650 | { |
| 651 | casserr_t err; |
| 652 | size_t chunk_sample_count; |
| 653 | size_t bytes_per_sample; |
| 654 | size_t sample_bytes; |
| 655 | size_t samples_saved = 0; |
| 656 | double chunk_time_index; |
| 657 | double chunk_sample_period; |
| 658 | int channel; |
| 659 | UINT8 buffer[8192]; |
| 660 | |
| 661 | bytes_per_sample = waveform_bytes_per_sample(waveform_flags); |
| 662 | sample_bytes = bytes_per_sample * channels; |
| 663 | |
| 664 | while(samples_saved < sample_count) |
| 665 | { |
| 666 | chunk_sample_count = MIN(sizeof(buffer) / sample_bytes, (sample_count - samples_saved)); |
| 667 | chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count); |
| 668 | chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_saved); |
| 669 | |
| 670 | for (channel = 0; channel < channels; channel++) |
| 671 | { |
| 672 | err = cassette_get_samples(cassette, channel, chunk_time_index, chunk_sample_period, |
| 673 | chunk_sample_count, sample_bytes, &buffer[channel * bytes_per_sample], waveform_flags); |
| 674 | if (err) |
| 675 | return err; |
| 676 | } |
| 677 | |
| 678 | cassette_image_write(cassette, buffer, offset, chunk_sample_count * sample_bytes); |
| 679 | |
| 680 | offset += chunk_sample_count * sample_bytes; |
| 681 | samples_saved += chunk_sample_count; |
| 682 | } |
| 683 | return CASSETTE_ERROR_SUCCESS; |
| 684 | } |
| 685 | |
| 686 | |
| 687 | |
| 688 | /********************************************************************* |
| 689 | waveform accesses to/from the raw image |
| 690 | *********************************************************************/ |
| 691 | |
| 692 | static const INT8 *choose_wave(const struct CassetteModulation *modulation, size_t *wave_bytes_length) |
| 693 | { |
| 694 | static const INT8 square_wave[] = { -128, 127 }; |
| 695 | static const INT8 sine_wave[] = { 0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48 }; |
| 696 | |
| 697 | if (modulation->flags & CASSETTE_MODULATION_SINEWAVE) |
| 698 | { |
| 699 | *wave_bytes_length = ARRAY_LENGTH(sine_wave); |
| 700 | return sine_wave; |
| 701 | } |
| 702 | else |
| 703 | { |
| 704 | *wave_bytes_length = ARRAY_LENGTH(square_wave); |
| 705 | return square_wave; |
| 706 | } |
| 707 | } |
| 708 | |
| 709 | |
| 710 | |
| 711 | casserr_t cassette_modulation_identify(cassette_image *cassette, const struct CassetteModulation *modulation, |
| 712 | struct CassetteOptions *opts) |
| 713 | { |
| 714 | size_t wave_bytes_length; |
| 715 | choose_wave(modulation, &wave_bytes_length); |
| 716 | opts->bits_per_sample = 8; |
| 717 | opts->channels = 1; |
| 718 | opts->sample_frequency = (UINT32) (MAX(modulation->zero_frequency_high, modulation->one_frequency_high) * wave_bytes_length * 2); |
| 719 | return CASSETTE_ERROR_SUCCESS; |
| 720 | } |
| 721 | |
| 722 | |
| 723 | |
| 724 | casserr_t cassette_put_modulated_data(cassette_image *cassette, int channel, double time_index, |
| 725 | const void *data, size_t data_length, const struct CassetteModulation *modulation, |
| 726 | double *time_displacement) |
| 727 | { |
| 728 | casserr_t err; |
| 729 | const UINT8 *data_bytes = (const UINT8 *)data; |
| 730 | const INT8 *wave_bytes; |
| 731 | size_t wave_bytes_length; |
| 732 | double total_displacement = 0.0; |
| 733 | double pulse_period; |
| 734 | double pulse_frequency; |
| 735 | UINT8 b; |
| 736 | int i; |
| 737 | |
| 738 | wave_bytes = choose_wave(modulation, &wave_bytes_length); |
| 739 | |
| 740 | while(data_length--) |
| 741 | { |
| 742 | b = *(data_bytes++); |
| 743 | for (i = 0; i < 8; i++) |
| 744 | { |
| 745 | pulse_frequency = (b & (1 << i)) ? modulation->one_frequency_cannonical : modulation->zero_frequency_cannonical; |
| 746 | pulse_period = 1 / pulse_frequency; |
| 747 | err = cassette_put_samples(cassette, 0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, CASSETTE_WAVEFORM_8BIT); |
| 748 | if (err) |
| 749 | goto done; |
| 750 | time_index += pulse_period; |
| 751 | total_displacement += pulse_period; |
| 752 | } |
| 753 | } |
| 754 | err = CASSETTE_ERROR_SUCCESS; |
| 755 | |
| 756 | done: |
| 757 | if (time_displacement) |
| 758 | *time_displacement = total_displacement; |
| 759 | return err; |
| 760 | } |
| 761 | |
| 762 | |
| 763 | |
| 764 | casserr_t cassette_put_modulated_filler(cassette_image *cassette, int channel, double time_index, |
| 765 | UINT8 filler, size_t filler_length, const struct CassetteModulation *modulation, |
| 766 | double *time_displacement) |
| 767 | { |
| 768 | casserr_t err; |
| 769 | double delta; |
| 770 | double total_displacement = 0.0; |
| 771 | |
| 772 | while(filler_length--) |
| 773 | { |
| 774 | err = cassette_put_modulated_data(cassette, channel, time_index, &filler, 1, modulation, &delta); |
| 775 | if (err) |
| 776 | return err; |
| 777 | total_displacement += delta; |
| 778 | time_index += delta; |
| 779 | } |
| 780 | |
| 781 | if (time_displacement) |
| 782 | *time_displacement = total_displacement; |
| 783 | return CASSETTE_ERROR_SUCCESS; |
| 784 | } |
| 785 | |
| 786 | |
| 787 | |
| 788 | casserr_t cassette_read_modulated_data(cassette_image *cassette, int channel, double time_index, |
| 789 | UINT64 offset, UINT64 length, const struct CassetteModulation *modulation, |
| 790 | double *time_displacement) |
| 791 | { |
| 792 | casserr_t err; |
| 793 | UINT8 buffer_stack[1024]; |
| 794 | UINT8 *buffer; |
| 795 | UINT8 *alloc_buffer = NULL; |
| 796 | double delta; |
| 797 | double total_displacement = 0.0; |
| 798 | size_t this_length; |
| 799 | size_t buffer_length; |
| 800 | |
| 801 | if (length <= sizeof(buffer_stack)) |
| 802 | { |
| 803 | buffer = buffer_stack; |
| 804 | buffer_length = sizeof(buffer_stack); |
| 805 | } |
| 806 | else |
| 807 | { |
| 808 | buffer_length = MIN(length, 100000); |
| 809 | alloc_buffer = (UINT8*)malloc(buffer_length); |
| 810 | if (!alloc_buffer) |
| 811 | { |
| 812 | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 813 | goto done; |
| 814 | } |
| 815 | buffer = alloc_buffer; |
| 816 | } |
| 817 | |
| 818 | while(length > 0) |
| 819 | { |
| 820 | this_length = (size_t) MIN(length, buffer_length); |
| 821 | cassette_image_read(cassette, buffer, offset, this_length); |
| 822 | |
| 823 | err = cassette_put_modulated_data(cassette, channel, time_index, buffer, this_length, modulation, &delta); |
| 824 | if (err) |
| 825 | goto done; |
| 826 | total_displacement += delta; |
| 827 | time_index += delta; |
| 828 | length -= this_length; |
| 829 | } |
| 830 | |
| 831 | if (time_displacement) |
| 832 | *time_displacement = total_displacement; |
| 833 | err = CASSETTE_ERROR_SUCCESS; |
| 834 | |
| 835 | done: |
| 836 | if (alloc_buffer) |
| 837 | free(alloc_buffer); |
| 838 | return err; |
| 839 | } |
| 840 | |
| 841 | |
| 842 | |
| 843 | casserr_t cassette_put_modulated_data_bit(cassette_image *cassette, int channel, double time_index, |
| 844 | UINT8 data, const struct CassetteModulation *modulation, |
| 845 | double *time_displacement) |
| 846 | { |
| 847 | casserr_t err; |
| 848 | const INT8 *wave_bytes; |
| 849 | size_t wave_bytes_length; |
| 850 | double total_displacement = 0.0; |
| 851 | double pulse_period; |
| 852 | double pulse_frequency; |
| 853 | |
| 854 | wave_bytes = choose_wave(modulation, &wave_bytes_length); |
| 855 | |
| 856 | pulse_frequency = (data) ? modulation->one_frequency_cannonical : modulation->zero_frequency_cannonical; |
| 857 | pulse_period = 1 / pulse_frequency; |
| 858 | err = cassette_put_samples(cassette, 0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, CASSETTE_WAVEFORM_8BIT); |
| 859 | if (err) |
| 860 | goto done; |
| 861 | time_index += pulse_period; |
| 862 | total_displacement += pulse_period; |
| 863 | |
| 864 | err = CASSETTE_ERROR_SUCCESS; |
| 865 | |
| 866 | done: |
| 867 | if (time_displacement) |
| 868 | *time_displacement = total_displacement; |
| 869 | return err; |
| 870 | } |
| 871 | |
| 872 | |
| 873 | |
| 874 | /********************************************************************* |
| 875 | waveform accesses to/from the raw image |
| 876 | *********************************************************************/ |
| 877 | |
| 878 | casserr_t cassette_legacy_identify(cassette_image *cassette, struct CassetteOptions *opts, |
| 879 | const struct CassetteLegacyWaveFiller *legacy_args) |
| 880 | { |
| 881 | opts->channels = 1; |
| 882 | opts->bits_per_sample = 16; |
| 883 | opts->sample_frequency = legacy_args->sample_frequency; |
| 884 | return CASSETTE_ERROR_SUCCESS; |
| 885 | } |
| 886 | |
| 887 | |
| 888 | |
| 889 | casserr_t cassette_legacy_construct(cassette_image *cassette, |
| 890 | const struct CassetteLegacyWaveFiller *legacy_args) |
| 891 | { |
| 892 | casserr_t err; |
| 893 | int length; |
| 894 | int sample_count; |
| 895 | void *bytes = NULL; |
| 896 | void *chunk = NULL; |
| 897 | INT16 *samples = NULL; |
| 898 | int pos = 0; |
| 899 | UINT64 offset = 0; |
| 900 | UINT64 size; |
| 901 | struct CassetteLegacyWaveFiller args; |
| 902 | |
| 903 | /* sanity check the args */ |
| 904 | assert(legacy_args->header_samples >= -1); |
| 905 | assert(legacy_args->trailer_samples >= 0); |
| 906 | assert(legacy_args->fill_wave); |
| 907 | |
| 908 | size = cassette_image_size(cassette); |
| 909 | |
| 910 | /* normalize the args */ |
| 911 | args = *legacy_args; |
| 912 | if (args.chunk_size == 0) |
| 913 | args.chunk_size = 1; |
| 914 | else if (args.chunk_size < 0) |
| 915 | args.chunk_size = cassette_image_size(cassette); |
| 916 | if (args.sample_frequency == 0) |
| 917 | args.sample_frequency = 11025; |
| 918 | |
| 919 | /* allocate a buffer for the binary data */ |
| 920 | chunk = malloc(args.chunk_size); |
| 921 | if (!chunk) |
| 922 | { |
| 923 | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 924 | goto done; |
| 925 | } |
| 926 | |
| 927 | /* determine number of samples */ |
| 928 | if (args.chunk_sample_calc) |
| 929 | { |
| 930 | if (size > 0x7FFFFFFF) |
| 931 | { |
| 932 | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 933 | goto done; |
| 934 | } |
| 935 | |
| 936 | bytes = malloc(size); |
| 937 | if (!bytes) |
| 938 | { |
| 939 | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 940 | goto done; |
| 941 | } |
| 942 | cassette_image_read(cassette, bytes, 0, size); |
| 943 | sample_count = args.chunk_sample_calc((const UINT8*)bytes, (int) size); |
| 944 | |
| 945 | if (args.header_samples < 0) |
| 946 | args.header_samples = sample_count; |
| 947 | } |
| 948 | else |
| 949 | { |
| 950 | sample_count = ((size + args.chunk_size - 1) / args.chunk_size) |
| 951 | * args.chunk_samples; |
| 952 | } |
| 953 | sample_count += args.header_samples + args.trailer_samples; |
| 954 | |
| 955 | /* allocate a buffer for the completed samples */ |
| 956 | samples = (INT16*)malloc(sample_count * sizeof(*samples)); |
| 957 | if (!samples) |
| 958 | { |
| 959 | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 960 | goto done; |
| 961 | } |
| 962 | |
| 963 | /* if there has to be a header */ |
| 964 | if (args.header_samples > 0) |
| 965 | { |
| 966 | length = args.fill_wave(samples + pos, sample_count - pos, CODE_HEADER); |
| 967 | if (length < 0) |
| 968 | { |
| 969 | err = CASSETTE_ERROR_INVALIDIMAGE; |
| 970 | goto done; |
| 971 | } |
| 972 | pos += length; |
| 973 | } |
| 974 | |
| 975 | /* convert the file data to samples */ |
| 976 | while((pos < sample_count) && (offset < size)) |
| 977 | { |
| 978 | cassette_image_read(cassette, chunk, offset, args.chunk_size); |
| 979 | offset += args.chunk_size; |
| 980 | |
| 981 | length = args.fill_wave(samples + pos, sample_count - pos, (UINT8*)chunk); |
| 982 | if (length < 0) |
| 983 | { |
| 984 | err = CASSETTE_ERROR_INVALIDIMAGE; |
| 985 | goto done; |
| 986 | } |
| 987 | pos += length; |
| 988 | if (length == 0) |
| 989 | break; |
| 990 | } |
| 991 | |
| 992 | /* if there has to be a trailer */ |
| 993 | if (args.trailer_samples > 0) |
| 994 | { |
| 995 | length = args.fill_wave(samples + pos, sample_count - pos, CODE_TRAILER); |
| 996 | if (length < 0) |
| 997 | { |
| 998 | err = CASSETTE_ERROR_INVALIDIMAGE; |
| 999 | goto done; |
| 1000 | } |
| 1001 | pos += length; |
| 1002 | } |
| 1003 | |
| 1004 | /* specify the wave */ |
| 1005 | err = cassette_put_samples(cassette, 0, 0.0, ((double) pos) / args.sample_frequency, |
| 1006 | pos, 2, samples, CASSETTE_WAVEFORM_16BIT); |
| 1007 | if (err) |
| 1008 | goto done; |
| 1009 | |
| 1010 | /* success! */ |
| 1011 | err = CASSETTE_ERROR_SUCCESS; |
| 1012 | |
| 1013 | #if DUMP_CASSETTES |
| 1014 | cassette_dump(cassette, "C:\\TEMP\\CASDUMP.WAV"); |
| 1015 | #endif |
| 1016 | |
| 1017 | done: |
| 1018 | if (samples) |
| 1019 | free(samples); |
| 1020 | if (chunk) |
| 1021 | free(chunk); |
| 1022 | if (bytes) |
| 1023 | free(bytes); |
| 1024 | return err; |
| 1025 | } |
| 1026 | |
| 1027 | |
| 1028 | |
| 1029 | /********************************************************************* |
| 1030 | cassette_dump |
| 1031 | |
| 1032 | A debugging call to dump a casette image to a disk based wave file |
| 1033 | *********************************************************************/ |
| 1034 | |
| 1035 | void cassette_dump(cassette_image *image, const char *filename) |
| 1036 | { |
| 1037 | FILE *f; |
| 1038 | struct io_generic saved_io; |
| 1039 | const struct CassetteFormat *saved_format; |
| 1040 | |
| 1041 | f = fopen(filename, "wb"); |
| 1042 | if (!f) |
| 1043 | return; |
| 1044 | |
| 1045 | memcpy(&saved_io, &image->io, sizeof(saved_io)); |
| 1046 | saved_format = image->format; |
| 1047 | |
| 1048 | image->io.file = f; |
| 1049 | image->io.procs = &stdio_ioprocs_noclose; |
| 1050 | image->format = &wavfile_format; |
| 1051 | cassette_perform_save(image); |
| 1052 | |
| 1053 | memcpy(&image->io, &saved_io, sizeof(saved_io)); |
| 1054 | image->format = saved_format; |
| 1055 | |
| 1056 | fclose(f); |
| 1057 | } |
| 1058 | |
| 1059 | |
src/emu/imagedev/bitbngr.c
| r0 | r9928 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | bitbngr.c |
| 4 | |
| 5 | TRS style "bitbanger" serial port |
| 6 | |
| 7 | *********************************************************************/ |
| 8 | |
| 9 | #include "emu.h" |
| 10 | #include "bitbngr.h" |
| 11 | |
| 12 | |
| 13 | |
| 14 | /*************************************************************************** |
| 15 | TYPE DEFINITIONS |
| 16 | ***************************************************************************/ |
| 17 | |
| 18 | typedef struct _bitbanger_token bitbanger_token; |
| 19 | struct _bitbanger_token |
| 20 | { |
| 21 | emu_timer *bitbanger_output_timer; |
| 22 | emu_timer *bitbanger_input_timer; |
| 23 | int output_value; |
| 24 | int build_count; |
| 25 | int build_byte; |
| 26 | attotime idle_delay; |
| 27 | attotime current_baud; |
| 28 | UINT32 input_buffer_size; |
| 29 | UINT32 input_buffer_cursor; |
| 30 | int mode; |
| 31 | int baud; |
| 32 | int tune; |
| 33 | UINT8 input_buffer[1000]; |
| 34 | }; |
| 35 | |
| 36 | |
| 37 | |
| 38 | /*************************************************************************** |
| 39 | FUNCTION PROTOTYPES |
| 40 | ***************************************************************************/ |
| 41 | |
| 42 | static TIMER_CALLBACK(bitbanger_output_timer); |
| 43 | static TIMER_CALLBACK(bitbanger_input_timer); |
| 44 | |
| 45 | |
| 46 | |
| 47 | /*************************************************************************** |
| 48 | INLINE FUNCTIONS |
| 49 | ***************************************************************************/ |
| 50 | |
| 51 | |
| 52 | /*------------------------------------------------- |
| 53 | get_token - safely gets the bitbanger data |
| 54 | -------------------------------------------------*/ |
| 55 | |
| 56 | INLINE bitbanger_token *get_token(device_t *device) |
| 57 | { |
| 58 | assert(device != NULL); |
| 59 | assert(device->type() == BITBANGER); |
| 60 | return (bitbanger_token *) downcast<legacy_device_base *>(device)->token(); |
| 61 | } |
| 62 | |
| 63 | |
| 64 | /*------------------------------------------------- |
| 65 | get_config - safely gets the bitbanger config |
| 66 | -------------------------------------------------*/ |
| 67 | |
| 68 | INLINE const bitbanger_config *get_config(device_t *device) |
| 69 | { |
| 70 | assert(device != NULL); |
| 71 | assert(device->type() == BITBANGER); |
| 72 | return (const bitbanger_config *) device->baseconfig().static_config(); |
| 73 | } |
| 74 | |
| 75 | |
| 76 | |
| 77 | /*************************************************************************** |
| 78 | IMPLEMENTATION |
| 79 | ***************************************************************************/ |
| 80 | |
| 81 | |
| 82 | /*------------------------------------------------- |
| 83 | native_output - outputs data to a file |
| 84 | -------------------------------------------------*/ |
| 85 | |
| 86 | static void native_output(device_t *bitbanger, UINT8 data) |
| 87 | { |
| 88 | device_image_interface *image = dynamic_cast<device_image_interface *>(bitbanger); |
| 89 | if (image->exists()) |
| 90 | { |
| 91 | image->fwrite(&data, 1); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | |
| 96 | /*------------------------------------------------- |
| 97 | native_input - inputs data from a file |
| 98 | -------------------------------------------------*/ |
| 99 | |
| 100 | static UINT32 native_input(device_t *bitbanger, void *buffer, UINT32 length) |
| 101 | { |
| 102 | device_image_interface *image = dynamic_cast<device_image_interface *>(bitbanger); |
| 103 | if (image->exists()) |
| 104 | { |
| 105 | return image->fread(buffer, length); |
| 106 | } |
| 107 | |
| 108 | return 0; |
| 109 | } |
| 110 | |
| 111 | |
| 112 | /*------------------------------------------------- |
| 113 | bitbanger_mode_string |
| 114 | -------------------------------------------------*/ |
| 115 | const char *bitbanger_mode_string(device_t *device) |
| 116 | { |
| 117 | bitbanger_token *bi = get_token(device); |
| 118 | static const char *const modes[] = {"Printer", "Modem"}; |
| 119 | |
| 120 | return(modes[bi->mode]); |
| 121 | } |
| 122 | |
| 123 | |
| 124 | /*------------------------------------------------- |
| 125 | bitbanger_inc_mode |
| 126 | -------------------------------------------------*/ |
| 127 | bool bitbanger_inc_mode(device_t *device, bool test) |
| 128 | { |
| 129 | bitbanger_token *bi = get_token(device); |
| 130 | int adjust_mode = (int)bi->mode + 1; |
| 131 | |
| 132 | if( adjust_mode >= BITBANGER_MODE_MAX ) |
| 133 | return FALSE; |
| 134 | |
| 135 | if( !test) |
| 136 | bi->mode = adjust_mode; |
| 137 | |
| 138 | return TRUE; |
| 139 | } |
| 140 | |
| 141 | |
| 142 | /*------------------------------------------------- |
| 143 | bitbanger_dec_mode |
| 144 | -------------------------------------------------*/ |
| 145 | bool bitbanger_dec_mode(device_t *device, bool test) |
| 146 | { |
| 147 | bitbanger_token *bi = get_token(device); |
| 148 | int adjust_mode = bi->mode - 1; |
| 149 | |
| 150 | if( adjust_mode < 0 ) |
| 151 | return FALSE; |
| 152 | |
| 153 | if( !test) |
| 154 | bi->mode = adjust_mode; |
| 155 | |
| 156 | return TRUE; |
| 157 | } |
| 158 | |
| 159 | |
| 160 | /*------------------------------------------------- |
| 161 | bitbanger_tune_string |
| 162 | -------------------------------------------------*/ |
| 163 | const char *bitbanger_tune_string(device_t *device) |
| 164 | { |
| 165 | bitbanger_token *bi = get_token(device); |
| 166 | static const char *const tunes[] = {"-2.0%", "-1.75%", "-1.5%", "-1.25%", "-1.0%", "-0.75%", "-0.5", "-0.25%", "\xc2\xb1""0%", |
| 167 | "+0.25%", "+0.5%", "+0.75%", "+1.0%", "+1.25%", "+1.5%", "+1.75%", "+2.0%"}; |
| 168 | |
| 169 | return(tunes[bi->tune]); |
| 170 | } |
| 171 | |
| 172 | |
| 173 | /*------------------------------------------------- |
| 174 | bitbanger_tune_value |
| 175 | -------------------------------------------------*/ |
| 176 | static float bitbanger_tune_value(device_t *device) |
| 177 | { |
| 178 | bitbanger_token *bi = get_token(device); |
| 179 | static const float tunes[] = {0.97f, 0.9825f, 0.985f, 0.9875f, 0.99f, 0.9925f, 0.995f, 0.9975f, 1.0f, |
| 180 | 1.0025f, 1.005f, 1.0075f, 1.01f, 1.0125f, 1.015f, 1.0175f, 1.02f}; |
| 181 | |
| 182 | return(tunes[bi->tune]); |
| 183 | } |
| 184 | |
| 185 | |
| 186 | /*------------------------------------------------- |
| 187 | bitbanger_baud_value |
| 188 | -------------------------------------------------*/ |
| 189 | |
| 190 | static UINT32 bitbanger_baud_value(device_t *device) |
| 191 | { |
| 192 | bitbanger_token *bi = get_token(device); |
| 193 | static const float bauds[] = { 150.0f, 300.0f, 600.0f, 1200.0f, 2400.0f, 4800.0f, 9600.0f, |
| 194 | 14400.0f, 28800.0f, 38400.0f, 57600.0f, 115200.0f}; |
| 195 | float result = bitbanger_tune_value(device) * bauds[bi->baud]; |
| 196 | return (UINT32)result; |
| 197 | } |
| 198 | |
| 199 | |
| 200 | /*------------------------------------------------- |
| 201 | bitbanger_baud_string |
| 202 | -------------------------------------------------*/ |
| 203 | |
| 204 | const char *bitbanger_baud_string(device_t *device) |
| 205 | { |
| 206 | bitbanger_token *bi = get_token(device); |
| 207 | static const char *const bauds[] = { "150", "300", "600", "1200", "2400", "4800", |
| 208 | "9600", "14400", "28800", "38400", "57600", "115200"}; |
| 209 | |
| 210 | return(bauds[bi->baud]); |
| 211 | } |
| 212 | |
| 213 | |
| 214 | /*------------------------------------------------- |
| 215 | bitbanger_inc_baud |
| 216 | -------------------------------------------------*/ |
| 217 | bool bitbanger_inc_baud(device_t *device, bool test) |
| 218 | { |
| 219 | bitbanger_token *bi = get_token(device); |
| 220 | int adjust_baud = (int)bi->baud + 1; |
| 221 | |
| 222 | if( adjust_baud >= BITBANGER_BAUD_MAX ) |
| 223 | return FALSE; |
| 224 | |
| 225 | if( !test) |
| 226 | { |
| 227 | bi->baud = adjust_baud; |
| 228 | bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device)); |
| 229 | } |
| 230 | |
| 231 | return TRUE; |
| 232 | } |
| 233 | |
| 234 | |
| 235 | /*------------------------------------------------- |
| 236 | bitbanger_dec_baud |
| 237 | -------------------------------------------------*/ |
| 238 | bool bitbanger_dec_baud(device_t *device, bool test) |
| 239 | { |
| 240 | bitbanger_token *bi = get_token(device); |
| 241 | int adjust_baud = bi->baud - 1; |
| 242 | |
| 243 | if( adjust_baud < 0 ) |
| 244 | return FALSE; |
| 245 | |
| 246 | if( !test) |
| 247 | { |
| 248 | bi->baud = adjust_baud; |
| 249 | bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device)); |
| 250 | } |
| 251 | |
| 252 | return TRUE; |
| 253 | } |
| 254 | |
| 255 | |
| 256 | /*------------------------------------------------- |
| 257 | bitbanger_inc_tune |
| 258 | -------------------------------------------------*/ |
| 259 | bool bitbanger_inc_tune(device_t *device, bool test) |
| 260 | { |
| 261 | bitbanger_token *bi = get_token(device); |
| 262 | int adjust_tune = (int)bi->tune + 1; |
| 263 | |
| 264 | if( adjust_tune >= BITBANGER_TUNE_MAX ) |
| 265 | return FALSE; |
| 266 | |
| 267 | if( !test) |
| 268 | { |
| 269 | bi->tune = adjust_tune; |
| 270 | bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device)); |
| 271 | } |
| 272 | |
| 273 | return TRUE; |
| 274 | } |
| 275 | |
| 276 | |
| 277 | /*------------------------------------------------- |
| 278 | bitbanger_dec_tune |
| 279 | -------------------------------------------------*/ |
| 280 | bool bitbanger_dec_tune(device_t *device, bool test) |
| 281 | { |
| 282 | bitbanger_token *bi = get_token(device); |
| 283 | int adjust_tune = bi->tune - 1; |
| 284 | |
| 285 | if( adjust_tune < 0 ) |
| 286 | return FALSE; |
| 287 | |
| 288 | if( !test) |
| 289 | { |
| 290 | bi->tune = adjust_tune; |
| 291 | bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device)); |
| 292 | } |
| 293 | |
| 294 | return TRUE; |
| 295 | } |
| 296 | |
| 297 | |
| 298 | /*------------------------------------------------- |
| 299 | bitbanger_bytes_to_bits_81N |
| 300 | -------------------------------------------------*/ |
| 301 | static void bitbanger_bytes_to_bits_81N(device_t *img) |
| 302 | { |
| 303 | bitbanger_token *bi = get_token(img); |
| 304 | UINT8 byte_buffer[100]; |
| 305 | UINT32 byte_buffer_size, bit_buffer_size; |
| 306 | int i, j; |
| 307 | static const UINT8 bitmask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; |
| 308 | |
| 309 | bit_buffer_size = 0; |
| 310 | byte_buffer_size = native_input(img, byte_buffer, sizeof(byte_buffer)); |
| 311 | |
| 312 | /* Translate byte buffer into bit buffer using: 1 start bit, 8 data bits, 1 stop bit, no parity */ |
| 313 | |
| 314 | for( i=0; i<byte_buffer_size; i++ ) |
| 315 | { |
| 316 | bi->input_buffer[bit_buffer_size++] = 0; |
| 317 | |
| 318 | for( j=0; j<8; j++ ) |
| 319 | { |
| 320 | if( byte_buffer[i] & bitmask[j] ) |
| 321 | bi->input_buffer[bit_buffer_size++] = 1; |
| 322 | else |
| 323 | bi->input_buffer[bit_buffer_size++] = 0; |
| 324 | } |
| 325 | |
| 326 | bi->input_buffer[bit_buffer_size++] = 1; |
| 327 | } |
| 328 | |
| 329 | bi->input_buffer_size = bit_buffer_size; |
| 330 | bi->input_buffer_cursor = 0; |
| 331 | } |
| 332 | |
| 333 | |
| 334 | /*------------------------------------------------- |
| 335 | DEVICE_START(bitbanger) |
| 336 | -------------------------------------------------*/ |
| 337 | |
| 338 | static DEVICE_START(bitbanger) |
| 339 | { |
| 340 | bitbanger_token *bi; |
| 341 | const bitbanger_config *config = get_config(device); |
| 342 | |
| 343 | bi = get_token(device); |
| 344 | |
| 345 | /* output config */ |
| 346 | bi->build_count = 0; |
| 347 | bi->bitbanger_output_timer = timer_alloc(device->machine, bitbanger_output_timer, (void *) device); |
| 348 | |
| 349 | /* input config */ |
| 350 | bi->bitbanger_input_timer = timer_alloc(device->machine, bitbanger_input_timer, (void *) device ); |
| 351 | bi->idle_delay = ATTOTIME_IN_SEC(1); |
| 352 | bi->input_buffer_size = 0; |
| 353 | bi->input_buffer_cursor = 0; |
| 354 | |
| 355 | bi->mode = config->default_mode; |
| 356 | bi->baud = config->default_baud; |
| 357 | bi->tune = config->default_tune; |
| 358 | bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device)); |
| 359 | |
| 360 | /* test callback */ |
| 361 | if(!config->input_callback) |
| 362 | fatalerror("Misconfigured bitbanger device: input_callback cannot be NULL\n"); |
| 363 | } |
| 364 | |
| 365 | |
| 366 | /*------------------------------------------------- |
| 367 | TIMER_CALLBACK(bitbanger_output_timer) |
| 368 | -------------------------------------------------*/ |
| 369 | |
| 370 | static TIMER_CALLBACK(bitbanger_output_timer) |
| 371 | { |
| 372 | device_t *device = (device_t *) ptr; |
| 373 | bitbanger_token *bi = get_token(device); |
| 374 | |
| 375 | /* this ia harded coded for 8-1-N */ |
| 376 | if( bi->output_value ) |
| 377 | bi->build_byte |= 0x200; |
| 378 | |
| 379 | bi->build_byte >>= 1; |
| 380 | bi->build_count--; |
| 381 | |
| 382 | if(bi->build_count == 0) |
| 383 | { |
| 384 | if( bi->output_value == 1 ) |
| 385 | native_output( device, bi->build_byte ); |
| 386 | else |
| 387 | logerror("Bitbanger: Output framing error.\n" ); |
| 388 | |
| 389 | timer_reset(bi->bitbanger_output_timer, attotime_never); |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | |
| 394 | /*------------------------------------------------- |
| 395 | TIMER_CALLBACK(bitbanger_input_timer) |
| 396 | -------------------------------------------------*/ |
| 397 | |
| 398 | static TIMER_CALLBACK(bitbanger_input_timer) |
| 399 | { |
| 400 | device_t *device = (device_t *) ptr; |
| 401 | bitbanger_token *bi = get_token(device); |
| 402 | const bitbanger_config *config = get_config(device); |
| 403 | |
| 404 | if(bi->input_buffer_cursor == bi->input_buffer_size) |
| 405 | { |
| 406 | /* get more data */ |
| 407 | bitbanger_bytes_to_bits_81N(device); |
| 408 | |
| 409 | if(bi->input_buffer_size == 0) |
| 410 | { |
| 411 | /* no more data, wait and try again */ |
| 412 | bi->idle_delay = attotime_min(attotime_add(bi->idle_delay, ATTOTIME_IN_MSEC(100)), ATTOTIME_IN_SEC(1)); |
| 413 | timer_adjust_oneshot(bi->bitbanger_input_timer, bi->idle_delay, 0); |
| 414 | |
| 415 | |
| 416 | if( bi->mode == BITBANGER_MODEM ) |
| 417 | config->input_callback(machine, 1); |
| 418 | else |
| 419 | config->input_callback(machine, 0); |
| 420 | |
| 421 | return; |
| 422 | } |
| 423 | else |
| 424 | { |
| 425 | bi->idle_delay = bi->current_baud; |
| 426 | timer_adjust_periodic(bi->bitbanger_input_timer, bi->idle_delay, 0, bi->idle_delay); |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | /* send bit to driver */ |
| 431 | config->input_callback(machine, bi->input_buffer[(bi->input_buffer_cursor)++]); |
| 432 | } |
| 433 | |
| 434 | |
| 435 | /*------------------------------------------------- |
| 436 | bitbanger_output - outputs data to a bitbanger |
| 437 | port |
| 438 | -------------------------------------------------*/ |
| 439 | |
| 440 | void bitbanger_output(device_t *device, int value) |
| 441 | { |
| 442 | bitbanger_token *bi = get_token(device); |
| 443 | attotime one_point_five_baud; |
| 444 | |
| 445 | if( bi->build_count == 0 && bi->output_value == 1 && value == 0 ) |
| 446 | { |
| 447 | /* we found our start bit */ |
| 448 | /* eight bits of data, plus one of stop */ |
| 449 | bi->build_count = 9; |
| 450 | bi->build_byte = 0; |
| 451 | |
| 452 | one_point_five_baud = attotime_add(bi->current_baud, attotime_div(bi->current_baud,2)); |
| 453 | timer_adjust_periodic(bi->bitbanger_output_timer, one_point_five_baud, 0, bi->current_baud); |
| 454 | } |
| 455 | |
| 456 | //fprintf(stderr,"%s, %d\n", attotime_string(timer_get_time(device->machine),9), value); |
| 457 | bi->output_value = value; |
| 458 | } |
| 459 | |
| 460 | |
| 461 | /*------------------------------------------------- |
| 462 | DEVICE_IMAGE_LOAD( bitbanger ) |
| 463 | -------------------------------------------------*/ |
| 464 | |
| 465 | static DEVICE_IMAGE_LOAD( bitbanger ) |
| 466 | { |
| 467 | device_t *device = &image.device(); |
| 468 | bitbanger_token *bi; |
| 469 | bi = get_token(device); |
| 470 | |
| 471 | timer_enable(bi->bitbanger_input_timer, TRUE); |
| 472 | timer_adjust_periodic(bi->bitbanger_input_timer, attotime_zero, 0, ATTOTIME_IN_SEC(1)); |
| 473 | |
| 474 | /* we don't need to do anything special */ |
| 475 | return IMAGE_INIT_PASS; |
| 476 | } |
| 477 | |
| 478 | |
| 479 | /*------------------------------------------------- |
| 480 | DEVICE_IMAGE_UNLOAD( bitbanger ) |
| 481 | -------------------------------------------------*/ |
| 482 | |
| 483 | static DEVICE_IMAGE_UNLOAD( bitbanger ) |
| 484 | { |
| 485 | device_t *device = &image.device(); |
| 486 | bitbanger_token *bi; |
| 487 | bi = get_token(device); |
| 488 | |
| 489 | timer_enable(bi->bitbanger_input_timer, FALSE); |
| 490 | } |
| 491 | |
| 492 | |
| 493 | /*------------------------------------------------- |
| 494 | DEVICE_GET_INFO(bitbanger) - device getinfo |
| 495 | function |
| 496 | -------------------------------------------------*/ |
| 497 | |
| 498 | DEVICE_GET_INFO(bitbanger) |
| 499 | { |
| 500 | switch(state) |
| 501 | { |
| 502 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 503 | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(bitbanger_token); break; |
| 504 | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; |
| 505 | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_PRINTER; break; |
| 506 | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 507 | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break; |
| 508 | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break; |
| 509 | |
| 510 | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 511 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(bitbanger); break; |
| 512 | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(bitbanger); break; |
| 513 | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(bitbanger); break; |
| 514 | |
| 515 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 516 | case DEVINFO_STR_NAME: strcpy(info->s, "Bitbanger"); break; |
| 517 | case DEVINFO_STR_FAMILY: strcpy(info->s, "Bitbanger"); break; |
| 518 | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 519 | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, "prn"); break; |
| 520 | } |
| 521 | } |
| 522 | |
| 523 | DEFINE_LEGACY_IMAGE_DEVICE(BITBANGER, bitbanger); |
src/emu/imagedev/cassimg.h
| r0 | r9928 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | cassimg.h |
| 4 | |
| 5 | Cassette tape image abstraction code |
| 6 | |
| 7 | *********************************************************************/ |
| 8 | |
| 9 | #ifndef CASSIMG_H |
| 10 | #define CASSIMG_H |
| 11 | |
| 12 | #include "pool.h" |
| 13 | #include "emu.h" |
| 14 | |
| 15 | /*************************************************************************** |
| 16 | |
| 17 | Constants |
| 18 | |
| 19 | ***************************************************************************/ |
| 20 | |
| 21 | #define CASSETTE_FLAG_READWRITE 0 |
| 22 | #define CASSETTE_FLAG_READONLY 1 |
| 23 | #define CASSETTE_FLAG_NOSAVEONEXIT 0 |
| 24 | #define CASSETTE_FLAG_SAVEONEXIT 2 |
| 25 | |
| 26 | #define CASSETTE_WAVEFORM_8BIT 0 |
| 27 | #define CASSETTE_WAVEFORM_16BIT 2 |
| 28 | #define CASSETTE_WAVEFORM_16BIT_FLIP 3 |
| 29 | #define CASSETTE_WAVEFORM_32BIT 4 |
| 30 | #define CASSETTE_WAVEFORM_32BIT_FLIP 5 |
| 31 | #define CASSETTE_WAVEFORM_ENDIAN_FLIP 1 |
| 32 | |
| 33 | #define CASSETTE_MODULATION_SQUAREWAVE 0 |
| 34 | #define CASSETTE_MODULATION_SINEWAVE 1 |
| 35 | |
| 36 | |
| 37 | #ifdef LSB_FIRST |
| 38 | #define CASSETTE_WAVEFORM_16BITBE CASSETTE_WAVEFORM_16BIT_FLIP |
| 39 | #define CASSETTE_WAVEFORM_16BITLE CASSETTE_WAVEFORM_16BIT |
| 40 | #define CASSETTE_WAVEFORM_32BITBE CASSETTE_WAVEFORM_32BIT_FLIP |
| 41 | #define CASSETTE_WAVEFORM_32BITLE CASSETTE_WAVEFORM_32BIT |
| 42 | #else |
| 43 | #define CASSETTE_WAVEFORM_16BITBE CASSETTE_WAVEFORM_16BIT |
| 44 | #define CASSETTE_WAVEFORM_16BITLE CASSETTE_WAVEFORM_16BIT_FLIP |
| 45 | #define CASSETTE_WAVEFORM_32BITBE CASSETTE_WAVEFORM_32BIT |
| 46 | #define CASSETTE_WAVEFORM_32BITLE CASSETTE_WAVEFORM_32BIT_FLIP |
| 47 | #endif |
| 48 | |
| 49 | |
| 50 | /*************************************************************************** |
| 51 | |
| 52 | Type definitions |
| 53 | |
| 54 | ***************************************************************************/ |
| 55 | |
| 56 | typedef enum |
| 57 | { |
| 58 | CASSETTE_ERROR_SUCCESS, /* no error */ |
| 59 | CASSETTE_ERROR_INTERNAL, /* fatal internal error */ |
| 60 | CASSETTE_ERROR_UNSUPPORTED, /* this operation is unsupported */ |
| 61 | CASSETTE_ERROR_OUTOFMEMORY, /* ran out of memory */ |
| 62 | CASSETTE_ERROR_INVALIDIMAGE, /* invalid image */ |
| 63 | CASSETTE_ERROR_INVALIDARG |
| 64 | } |
| 65 | casserr_t; |
| 66 | |
| 67 | struct sample_block |
| 68 | { |
| 69 | INT32 *block; |
| 70 | size_t sample_count; |
| 71 | }; |
| 72 | |
| 73 | struct CassetteOptions |
| 74 | { |
| 75 | int channels; |
| 76 | int bits_per_sample; |
| 77 | UINT32 sample_frequency; |
| 78 | }; |
| 79 | |
| 80 | struct CassetteInfo |
| 81 | { |
| 82 | int channels; |
| 83 | int bits_per_sample; |
| 84 | UINT32 sample_frequency; |
| 85 | size_t sample_count; |
| 86 | }; |
| 87 | |
| 88 | struct _cassette_image |
| 89 | { |
| 90 | const struct CassetteFormat *format; |
| 91 | struct io_generic io; |
| 92 | object_pool *pool; |
| 93 | |
| 94 | int channels; |
| 95 | int flags; |
| 96 | UINT32 sample_frequency; |
| 97 | |
| 98 | struct sample_block *blocks; |
| 99 | size_t block_count; |
| 100 | size_t sample_count; |
| 101 | |
| 102 | running_machine *machine; |
| 103 | }; |
| 104 | |
| 105 | typedef struct _cassette_image cassette_image; |
| 106 | |
| 107 | struct CassetteFormat |
| 108 | { |
| 109 | const char *extensions; |
| 110 | casserr_t (*identify)(cassette_image *cassette, struct CassetteOptions *opts); |
| 111 | casserr_t (*load)(cassette_image *cassette); |
| 112 | casserr_t (*save)(cassette_image *cassette, const struct CassetteInfo *info); |
| 113 | }; |
| 114 | |
| 115 | /* used for the core modulation code */ |
| 116 | struct CassetteModulation |
| 117 | { |
| 118 | int flags; |
| 119 | double zero_frequency_low; |
| 120 | double zero_frequency_cannonical; |
| 121 | double zero_frequency_high; |
| 122 | double one_frequency_low; |
| 123 | double one_frequency_cannonical; |
| 124 | double one_frequency_high; |
| 125 | }; |
| 126 | |
| 127 | /* code to adapt existing legacy fill_wave functions */ |
| 128 | struct CassetteLegacyWaveFiller |
| 129 | { |
| 130 | int (*fill_wave)(INT16 *, int, UINT8 *); |
| 131 | int chunk_size; |
| 132 | int chunk_samples; |
| 133 | int (*chunk_sample_calc)(const UINT8 *bytes, int length); |
| 134 | UINT32 sample_frequency; |
| 135 | int header_samples; |
| 136 | int trailer_samples; |
| 137 | }; |
| 138 | |
| 139 | /* builtin formats */ |
| 140 | extern const struct CassetteFormat wavfile_format; |
| 141 | |
| 142 | /* macros for specifying format lists */ |
| 143 | #define CASSETTE_FORMATLIST_EXTERN(name) \ |
| 144 | extern const struct CassetteFormat *const name[] |
| 145 | |
| 146 | #define CASSETTE_FORMATLIST_START(name) \ |
| 147 | const struct CassetteFormat *const name[] = \ |
| 148 | { \ |
| 149 | &wavfile_format, \ |
| 150 | |
| 151 | #define CASSETTE_FORMAT(name) \ |
| 152 | &(name), \ |
| 153 | |
| 154 | #define CASSETTE_FORMATLIST_END \ |
| 155 | NULL \ |
| 156 | }; |
| 157 | |
| 158 | CASSETTE_FORMATLIST_EXTERN(cassette_default_formats); |
| 159 | |
| 160 | /*************************************************************************** |
| 161 | |
| 162 | Prototypes |
| 163 | |
| 164 | ***************************************************************************/ |
| 165 | |
| 166 | casserr_t cassette_open(running_machine *machine,void *file, const struct io_procs *procs, |
| 167 | const struct CassetteFormat *format, int flags, cassette_image **outcassette); |
| 168 | casserr_t cassette_open_choices(running_machine *machine,void *file, const struct io_procs *procs, const char *extension, |
| 169 | const struct CassetteFormat *const *formats, int flags, cassette_image **outcassette); |
| 170 | casserr_t cassette_create(running_machine *machine,void *file, const struct io_procs *procs, const struct CassetteFormat *format, |
| 171 | const struct CassetteOptions *opts, int flags, cassette_image **outcassette); |
| 172 | casserr_t cassette_save(cassette_image *cassette); |
| 173 | void cassette_close(cassette_image *cassette); |
| 174 | void cassette_change(cassette_image *cassette, void *file, const struct io_procs *procs, const struct CassetteFormat *format, int flags); |
| 175 | void cassette_get_info(cassette_image *cassette, struct CassetteInfo *info); |
| 176 | |
| 177 | /* calls for accessing the raw cassette image */ |
| 178 | void cassette_image_read(cassette_image *cassette, void *buffer, UINT64 offset, size_t length); |
| 179 | void cassette_image_write(cassette_image *cassette, const void *buffer, UINT64 offset, size_t length); |
| 180 | UINT64 cassette_image_size(cassette_image *cassette); |
| 181 | |
| 182 | /* waveform accesses */ |
| 183 | casserr_t cassette_get_samples(cassette_image *cassette, int channel, |
| 184 | double time_index, double sample_period, size_t sample_count, size_t sample_bytes, |
| 185 | void *samples, int waveform_flags); |
| 186 | casserr_t cassette_put_samples(cassette_image *cassette, int channel, |
| 187 | double time_index, double sample_period, size_t sample_count, size_t sample_bytes, |
| 188 | const void *samples, int waveform_flags); |
| 189 | casserr_t cassette_get_sample(cassette_image *cassette, int channel, |
| 190 | double time_index, double sample_period, INT32 *sample); |
| 191 | casserr_t cassette_put_sample(cassette_image *cassette, int channel, |
| 192 | double time_index, double sample_period, INT32 sample); |
| 193 | |
| 194 | /* waveform accesses to/from the raw image */ |
| 195 | casserr_t cassette_read_samples(cassette_image *cassette, int channels, double time_index, |
| 196 | double sample_period, size_t sample_count, UINT64 offset, int waveform_flags); |
| 197 | casserr_t cassette_write_samples(cassette_image *cassette, int channels, double time_index, |
| 198 | double sample_period, size_t sample_count, UINT64 offset, int waveform_flags); |
| 199 | |
| 200 | /* modulation support */ |
| 201 | casserr_t cassette_modulation_identify(cassette_image *cassette, const struct CassetteModulation *modulation, |
| 202 | struct CassetteOptions *opts); |
| 203 | casserr_t cassette_put_modulated_data(cassette_image *cassette, int channel, double time_index, |
| 204 | const void *data, size_t data_length, const struct CassetteModulation *modulation, |
| 205 | double *time_displacement); |
| 206 | casserr_t cassette_put_modulated_filler(cassette_image *cassette, int channel, double time_index, |
| 207 | UINT8 filler, size_t filler_length, const struct CassetteModulation *modulation, |
| 208 | double *time_displacement); |
| 209 | casserr_t cassette_read_modulated_data(cassette_image *cassette, int channel, double time_index, |
| 210 | UINT64 offset, UINT64 length, const struct CassetteModulation *modulation, |
| 211 | double *time_displacement); |
| 212 | casserr_t cassette_put_modulated_data_bit(cassette_image *cassette, int channel, double time_index, |
| 213 | UINT8 data, const struct CassetteModulation *modulation, |
| 214 | double *time_displacement); |
| 215 | |
| 216 | /* debug calls */ |
| 217 | void cassette_dump(cassette_image *image, const char *filename); |
| 218 | |
| 219 | /* legacy code support */ |
| 220 | #define CODE_HEADER ((UINT8*)-1) |
| 221 | #define CODE_TRAILER ((UINT8*)-2) |
| 222 | casserr_t cassette_legacy_identify(cassette_image *cassette, struct CassetteOptions *opts, |
| 223 | const struct CassetteLegacyWaveFiller *legacy_args); |
| 224 | casserr_t cassette_legacy_construct(cassette_image *cassette, |
| 225 | const struct CassetteLegacyWaveFiller *legacy_args); |
| 226 | |
| 227 | #endif /* CASSIMG_H */ |
src/emu/imagedev/cartslot.c
| r0 | r9928 | |
| 1 | /********************************************************************** |
| 2 | |
| 3 | cartslot.c |
| 4 | |
| 5 | Cartridge device |
| 6 | |
| 7 | **********************************************************************/ |
| 8 | |
| 9 | #include <ctype.h> |
| 10 | #include "emu.h" |
| 11 | #include "cartslot.h" |
| 12 | #include "multcart.h" |
| 13 | |
| 14 | /*************************************************************************** |
| 15 | CONSTANTS |
| 16 | ***************************************************************************/ |
| 17 | |
| 18 | enum _process_mode |
| 19 | { |
| 20 | PROCESS_CLEAR, |
| 21 | PROCESS_LOAD |
| 22 | }; |
| 23 | typedef enum _process_mode process_mode; |
| 24 | |
| 25 | |
| 26 | /*************************************************************************** |
| 27 | INLINE FUNCTIONS |
| 28 | ***************************************************************************/ |
| 29 | |
| 30 | INLINE cartslot_t *get_token(device_t *device) |
| 31 | { |
| 32 | assert(device != NULL); |
| 33 | assert(device->type() == CARTSLOT); |
| 34 | return (cartslot_t *) downcast<legacy_device_base *>(device)->token(); |
| 35 | } |
| 36 | |
| 37 | |
| 38 | INLINE const cartslot_config *get_config(device_t *device) |
| 39 | { |
| 40 | assert(device != NULL); |
| 41 | assert(device->type() == CARTSLOT); |
| 42 | return (const cartslot_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config(); |
| 43 | } |
| 44 | |
| 45 | INLINE const cartslot_config *get_config_dev(const device_config *device) |
| 46 | { |
| 47 | assert(device != NULL); |
| 48 | assert(device->type() == CARTSLOT); |
| 49 | return (const cartslot_config *)downcast<const legacy_device_config_base *>(device)->inline_config(); |
| 50 | } |
| 51 | |
| 52 | /*************************************************************************** |
| 53 | IMPLEMENTATION |
| 54 | ***************************************************************************/ |
| 55 | |
| 56 | /*------------------------------------------------- |
| 57 | load_cartridge |
| 58 | -------------------------------------------------*/ |
| 59 | |
| 60 | static int load_cartridge(device_image_interface *image, const rom_entry *romrgn, const rom_entry *roment, process_mode mode) |
| 61 | { |
| 62 | const char *region; |
| 63 | const char *type; |
| 64 | UINT32 flags; |
| 65 | offs_t offset, length, read_length, pos = 0, len; |
| 66 | UINT8 *ptr; |
| 67 | UINT8 clear_val; |
| 68 | int datawidth, littleendian, i, j; |
| 69 | device_t *cpu; |
| 70 | |
| 71 | region = ROMREGION_GETTAG(romrgn); |
| 72 | offset = ROM_GETOFFSET(roment); |
| 73 | length = ROM_GETLENGTH(roment); |
| 74 | flags = ROM_GETFLAGS(roment); |
| 75 | ptr = ((UINT8 *) image->device().machine->region(region)->base()) + offset; |
| 76 | |
| 77 | if (mode == PROCESS_LOAD) |
| 78 | { |
| 79 | if (image->software_entry() == NULL) |
| 80 | { |
| 81 | /* must this be full size */ |
| 82 | if (flags & ROM_FULLSIZE) |
| 83 | { |
| 84 | if (image->length() != length) |
| 85 | return IMAGE_INIT_FAIL; |
| 86 | } |
| 87 | |
| 88 | /* read the ROM */ |
| 89 | pos = read_length = image->fread(ptr, length); |
| 90 | |
| 91 | /* reset the ROM to the initial point. */ |
| 92 | /* eventually, we could add a flag to allow the ROM to continue instead of restarting whenever a new cart region is present */ |
| 93 | image->fseek(0, SEEK_SET); |
| 94 | } |
| 95 | else |
| 96 | { |
| 97 | /* must this be full size */ |
| 98 | if (flags & ROM_FULLSIZE) |
| 99 | { |
| 100 | if (image->get_software_region_length("rom") != length) |
| 101 | return IMAGE_INIT_FAIL; |
| 102 | } |
| 103 | |
| 104 | /* read the ROM */ |
| 105 | pos = read_length = image->get_software_region_length("rom"); |
| 106 | memcpy(ptr, image->get_software_region("rom"), read_length); |
| 107 | } |
| 108 | |
| 109 | /* do we need to mirror the ROM? */ |
| 110 | if (flags & ROM_MIRROR) |
| 111 | { |
| 112 | while(pos < length) |
| 113 | { |
| 114 | len = MIN(read_length, length - pos); |
| 115 | memcpy(ptr + pos, ptr, len); |
| 116 | pos += len; |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | /* postprocess this region */ |
| 121 | type = ROMREGION_GETTAG(romrgn); |
| 122 | littleendian = ROMREGION_ISLITTLEENDIAN(romrgn); |
| 123 | datawidth = ROMREGION_GETWIDTH(romrgn) / 8; |
| 124 | |
| 125 | /* if the region is inverted, do that now */ |
| 126 | device_memory_interface *memory; |
| 127 | cpu = image->device().machine->device(type); |
| 128 | if (cpu!=NULL && cpu->interface(memory)) |
| 129 | { |
| 130 | datawidth = device_memory(cpu)->space_config(AS_PROGRAM)->m_databus_width / 8; |
| 131 | littleendian = (device_memory(cpu)->space_config()->m_endianness == ENDIANNESS_LITTLE); |
| 132 | } |
| 133 | |
| 134 | /* swap the endianness if we need to */ |
| 135 | #ifdef LSB_FIRST |
| 136 | if (datawidth > 1 && !littleendian) |
| 137 | #else |
| 138 | if (datawidth > 1 && littleendian) |
| 139 | #endif |
| 140 | { |
| 141 | for (i = 0; i < length; i += datawidth) |
| 142 | { |
| 143 | UINT8 temp[8]; |
| 144 | memcpy(temp, &ptr[i], datawidth); |
| 145 | for (j = datawidth - 1; j >= 0; j--) |
| 146 | ptr[i + j] = temp[datawidth - 1 - j]; |
| 147 | } |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | /* clear out anything that remains */ |
| 152 | if (!(flags & ROM_NOCLEAR)) |
| 153 | { |
| 154 | clear_val = (flags & ROM_FILL_FF) ? 0xFF : 0x00; |
| 155 | memset(ptr + pos, clear_val, length - pos); |
| 156 | } |
| 157 | return IMAGE_INIT_PASS; |
| 158 | } |
| 159 | |
| 160 | |
| 161 | |
| 162 | /*------------------------------------------------- |
| 163 | process_cartridge |
| 164 | -------------------------------------------------*/ |
| 165 | |
| 166 | static int process_cartridge(device_image_interface *image, process_mode mode) |
| 167 | { |
| 168 | const rom_source *source; |
| 169 | const rom_entry *romrgn, *roment; |
| 170 | int result = 0; |
| 171 | |
| 172 | for (source = rom_first_source(*image->device().machine->config); source != NULL; source = rom_next_source(*source)) |
| 173 | { |
| 174 | for (romrgn = rom_first_region(*source); romrgn != NULL; romrgn = rom_next_region(romrgn)) |
| 175 | { |
| 176 | roment = romrgn + 1; |
| 177 | while(!ROMENTRY_ISREGIONEND(roment)) |
| 178 | { |
| 179 | if (ROMENTRY_GETTYPE(roment) == ROMENTRYTYPE_CARTRIDGE) |
| 180 | { |
| 181 | if (strcmp(roment->_hashdata,image->device().tag())==0) |
| 182 | { |
| 183 | result |= load_cartridge(image, romrgn, roment, mode); |
| 184 | |
| 185 | /* if loading failed in any cart region, stop loading */ |
| 186 | if (result) |
| 187 | return result; |
| 188 | } |
| 189 | } |
| 190 | roment++; |
| 191 | } |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | return IMAGE_INIT_PASS; |
| 196 | } |
| 197 | |
| 198 | |
| 199 | /*------------------------------------------------- |
| 200 | cartslot_get_pcb |
| 201 | -------------------------------------------------*/ |
| 202 | |
| 203 | device_t *cartslot_get_pcb(device_t *device) |
| 204 | { |
| 205 | cartslot_t *cart = get_token(device); |
| 206 | return cart->pcb_device; |
| 207 | } |
| 208 | |
| 209 | |
| 210 | /*------------------------------------------------- |
| 211 | cartslot_get_socket |
| 212 | -------------------------------------------------*/ |
| 213 | |
| 214 | void *cartslot_get_socket(device_t *device, const char *socket_name) |
| 215 | { |
| 216 | cartslot_t *cart = get_token(device); |
| 217 | device_image_interface *image = dynamic_cast<device_image_interface *>(device); |
| 218 | void *result = NULL; |
| 219 | |
| 220 | if (cart->mc != NULL) |
| 221 | { |
| 222 | const multicart_socket *socket; |
| 223 | for (socket = cart->mc->sockets; socket != NULL; socket = socket->next) |
| 224 | { |
| 225 | if (!strcmp(socket->id, socket_name)) |
| 226 | break; |
| 227 | } |
| 228 | result = socket ? socket->ptr : NULL; |
| 229 | } |
| 230 | else if (socket_name[0] == '\0') |
| 231 | { |
| 232 | result = image->ptr(); |
| 233 | } |
| 234 | return result; |
| 235 | } |
| 236 | |
| 237 | /*------------------------------------------------- |
| 238 | cartslot_get_resource_length |
| 239 | -------------------------------------------------*/ |
| 240 | |
| 241 | int cartslot_get_resource_length(device_t *device, const char *socket_name) |
| 242 | { |
| 243 | cartslot_t *cart = get_token(device); |
| 244 | int result = 0; |
| 245 | |
| 246 | if (cart->mc != NULL) |
| 247 | { |
| 248 | const multicart_socket *socket; |
| 249 | |
| 250 | for (socket = cart->mc->sockets; socket != NULL; socket = socket->next) |
| 251 | { |
| 252 | if (!strcmp(socket->id, socket_name)) { |
| 253 | break; |
| 254 | } |
| 255 | } |
| 256 | if (socket != NULL) |
| 257 | result = socket->resource->length; |
| 258 | } |
| 259 | else |
| 260 | result = 0; |
| 261 | |
| 262 | return result; |
| 263 | } |
| 264 | |
| 265 | |
| 266 | /*------------------------------------------------- |
| 267 | DEVICE_START( cartslot ) |
| 268 | -------------------------------------------------*/ |
| 269 | |
| 270 | static DEVICE_START( cartslot ) |
| 271 | { |
| 272 | cartslot_t *cart = get_token(device); |
| 273 | const cartslot_config *config = get_config(device); |
| 274 | |
| 275 | /* if this cartridge has a custom DEVICE_START, use it */ |
| 276 | if (config->device_start != NULL) |
| 277 | { |
| 278 | (*config->device_start)(device); |
| 279 | goto done; |
| 280 | } |
| 281 | |
| 282 | /* find the PCB (if there is one) */ |
| 283 | cart->pcb_device = device->subdevice(TAG_PCB); |
| 284 | |
| 285 | done: |
| 286 | return; |
| 287 | } |
| 288 | |
| 289 | |
| 290 | /*------------------------------------------------- |
| 291 | DEVICE_IMAGE_LOAD( cartslot ) |
| 292 | -------------------------------------------------*/ |
| 293 | |
| 294 | static DEVICE_IMAGE_LOAD( cartslot ) |
| 295 | { |
| 296 | int result; |
| 297 | device_t *device = &image.device(); |
| 298 | cartslot_t *cart = get_token(device); |
| 299 | const cartslot_config *config = get_config(device); |
| 300 | |
| 301 | /* if this cartridge has a custom DEVICE_IMAGE_LOAD, use it */ |
| 302 | if (config->device_load != NULL) |
| 303 | return (*config->device_load)(image); |
| 304 | |
| 305 | /* try opening this as if it were a multicart */ |
| 306 | multicart_open(image.filename(), device->machine->gamedrv->name, MULTICART_FLAGS_LOAD_RESOURCES, &cart->mc); |
| 307 | if (cart->mc == NULL) |
| 308 | { |
| 309 | |
| 310 | |
| 311 | /* otherwise try the normal route */ |
| 312 | result = process_cartridge(&image, PROCESS_LOAD); |
| 313 | if (result != IMAGE_INIT_PASS) |
| 314 | return result; |
| 315 | } |
| 316 | |
| 317 | return IMAGE_INIT_PASS; |
| 318 | } |
| 319 | |
| 320 | |
| 321 | /*------------------------------------------------- |
| 322 | DEVICE_IMAGE_UNLOAD( cartslot ) |
| 323 | -------------------------------------------------*/ |
| 324 | |
| 325 | static DEVICE_IMAGE_UNLOAD( cartslot ) |
| 326 | { |
| 327 | device_t *device = &image.device(); |
| 328 | cartslot_t *cart = get_token(device); |
| 329 | const cartslot_config *config = get_config(device); |
| 330 | |
| 331 | /* if this cartridge has a custom DEVICE_IMAGE_UNLOAD, use it */ |
| 332 | if (config->device_unload != NULL) |
| 333 | { |
| 334 | (*config->device_unload)(image); |
| 335 | return; |
| 336 | } |
| 337 | |
| 338 | if (cart->mc != NULL) |
| 339 | { |
| 340 | multicart_close(cart->mc); |
| 341 | cart->mc = NULL; |
| 342 | } |
| 343 | |
| 344 | process_cartridge(&image, PROCESS_CLEAR); |
| 345 | } |
| 346 | |
| 347 | |
| 348 | /*------------------------------------------------- |
| 349 | identify_pcb |
| 350 | -------------------------------------------------*/ |
| 351 | |
| 352 | static const cartslot_pcb_type *identify_pcb(device_image_interface &image) |
| 353 | { |
| 354 | const cartslot_config *config = get_config(&image.device()); |
| 355 | astring pcb_name; |
| 356 | const cartslot_pcb_type *pcb_type = NULL; |
| 357 | multicart_t *mc; |
| 358 | int i; |
| 359 | |
| 360 | if (image.software_entry() == NULL && image.exists()) |
| 361 | { |
| 362 | /* try opening this as if it were a multicart */ |
| 363 | multicart_open_error me = multicart_open(image.filename(), image.device().machine->gamedrv->name, MULTICART_FLAGS_DONT_LOAD_RESOURCES, &mc); |
| 364 | if (me == MCERR_NONE) |
| 365 | { |
| 366 | /* this was a multicart - read from it */ |
| 367 | astring_cpyc(&pcb_name, mc->pcb_type); |
| 368 | multicart_close(mc); |
| 369 | } |
| 370 | else |
| 371 | { |
| 372 | if (me != MCERR_NOT_MULTICART) |
| 373 | fatalerror("multicart error: %s", multicart_error_text(me)); |
| 374 | |
| 375 | /* Force fetching of image metadata */ |
| 376 | image.crc(); |
| 377 | |
| 378 | if (image.pcb() != NULL) |
| 379 | { |
| 380 | /* read from hash file */ |
| 381 | astring_cpyc(&pcb_name, image.pcb()); |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | /* look for PCB type with matching name */ |
| 386 | for (i = 0; (i < ARRAY_LENGTH(config->pcb_types)) && (config->pcb_types[i].name != NULL); i++) |
| 387 | { |
| 388 | if ((config->pcb_types[i].name[0] == '\0') || !strcmp(astring_c(&pcb_name), config->pcb_types[i].name)) |
| 389 | { |
| 390 | pcb_type = &config->pcb_types[i]; |
| 391 | break; |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | /* check for unknown PCB type */ |
| 396 | if ((mc != NULL) && (pcb_type == NULL)) |
| 397 | fatalerror("Unknown PCB type \"%s\"", astring_c(&pcb_name)); |
| 398 | } |
| 399 | else |
| 400 | { |
| 401 | /* no device loaded; use the default */ |
| 402 | pcb_type = (config->pcb_types[0].name != NULL) ? &config->pcb_types[0] : NULL; |
| 403 | } |
| 404 | return pcb_type; |
| 405 | } |
| 406 | |
| 407 | /*------------------------------------------------- |
| 408 | DEVICE_IMAGE_GET_DEVICES(cartslot) |
| 409 | -------------------------------------------------*/ |
| 410 | static DEVICE_IMAGE_GET_DEVICES(cartslot) |
| 411 | { |
| 412 | const cartslot_pcb_type *pcb_type; |
| 413 | device_t *device = &image.device(); |
| 414 | |
| 415 | pcb_type = identify_pcb(image); |
| 416 | if (pcb_type != NULL) |
| 417 | { |
| 418 | image_add_device_with_subdevices(device,pcb_type->devtype,TAG_PCB,0); |
| 419 | } |
| 420 | } |
| 421 | |
| 422 | /*------------------------------------------------- |
| 423 | DEVICE_IMAGE_SOFTLIST_LOAD(cartslot) |
| 424 | -------------------------------------------------*/ |
| 425 | static DEVICE_IMAGE_SOFTLIST_LOAD(cartslot) |
| 426 | { |
| 427 | load_software_part_region( &image.device(), swlist, swname, start_entry ); |
| 428 | return TRUE; |
| 429 | } |
| 430 | |
| 431 | /*------------------------------------------------- |
| 432 | DEVICE_GET_INFO( cartslot ) |
| 433 | -------------------------------------------------*/ |
| 434 | |
| 435 | DEVICE_GET_INFO( cartslot ) |
| 436 | { |
| 437 | switch(state) |
| 438 | { |
| 439 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 440 | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(cartslot_t); break; |
| 441 | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(cartslot_config); break; |
| 442 | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_CARTSLOT; break; |
| 443 | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 444 | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 0; break; |
| 445 | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 0; break; |
| 446 | case DEVINFO_INT_IMAGE_RESET_ON_LOAD: info->i = 1; break; |
| 447 | case DEVINFO_INT_IMAGE_MUST_BE_LOADED: |
| 448 | if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config()) { |
| 449 | info->i = get_config_dev(device)->must_be_loaded; |
| 450 | } else { |
| 451 | info->i = 0; |
| 452 | } |
| 453 | break; |
| 454 | |
| 455 | /* --- the following bits of info are returned as pointers to functions --- */ |
| 456 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(cartslot); break; |
| 457 | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(cartslot); break; |
| 458 | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(cartslot); break; |
| 459 | case DEVINFO_FCT_IMAGE_GET_DEVICES: info->f = (genf *) DEVICE_IMAGE_GET_DEVICES_NAME(cartslot); break; |
| 460 | case DEVINFO_FCT_IMAGE_SOFTLIST_LOAD: info->f = (genf *) DEVICE_IMAGE_SOFTLIST_LOAD_NAME(cartslot); break; |
| 461 | case DEVINFO_FCT_IMAGE_PARTIAL_HASH: |
| 462 | if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config() && get_config_dev(device)->device_partialhash) { |
| 463 | info->f = (genf *) get_config_dev(device)->device_partialhash; |
| 464 | } else { |
| 465 | info->f = NULL; |
| 466 | } |
| 467 | break; |
| 468 | |
| 469 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 470 | case DEVINFO_STR_NAME: strcpy(info->s, "Cartslot"); break; |
| 471 | case DEVINFO_STR_FAMILY: strcpy(info->s, "Cartslot"); break; |
| 472 | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 473 | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: |
| 474 | if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config() && get_config_dev(device)->extensions ) |
| 475 | { |
| 476 | strcpy(info->s, get_config_dev(device)->extensions); |
| 477 | } |
| 478 | else |
| 479 | { |
| 480 | strcpy(info->s, "bin"); |
| 481 | } |
| 482 | break; |
| 483 | case DEVINFO_STR_IMAGE_INTERFACE: |
| 484 | if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config() && get_config_dev(device)->interface ) |
| 485 | { |
| 486 | strcpy(info->s, get_config_dev(device)->interface ); |
| 487 | } |
| 488 | break; |
| 489 | } |
| 490 | } |
| 491 | |
| 492 | DEFINE_LEGACY_IMAGE_DEVICE(CARTSLOT, cartslot); |
| 493 | |
| 494 | |
| 495 | //************************************************************************** |
| 496 | // DEVICE CONFIG CARTSLOT INTERFACE |
| 497 | //************************************************************************** |
| 498 | |
| 499 | //------------------------------------------------- |
| 500 | // device_config_cart_slot_interface - constructor |
| 501 | //------------------------------------------------- |
| 502 | |
| 503 | device_config_cart_slot_interface::device_config_cart_slot_interface(const machine_config &mconfig, device_config &devconfig) |
| 504 | : device_config_interface(mconfig, devconfig) |
| 505 | { |
| 506 | } |
| 507 | |
| 508 | |
| 509 | //------------------------------------------------- |
| 510 | // ~device_config_cart_slot_interface - destructor |
| 511 | //------------------------------------------------- |
| 512 | |
| 513 | device_config_cart_slot_interface::~device_config_cart_slot_interface() |
| 514 | { |
| 515 | } |
| 516 | |
| 517 | |
| 518 | |
| 519 | //************************************************************************** |
| 520 | // DEVICE CARTSLOT INTERFACE |
| 521 | //************************************************************************** |
| 522 | |
| 523 | //------------------------------------------------- |
| 524 | // device_cart_slot_interface - constructor |
| 525 | //------------------------------------------------- |
| 526 | |
| 527 | device_cart_slot_interface::device_cart_slot_interface(running_machine &machine, const device_config &config, device_t &device) |
| 528 | : device_interface(machine, config, device), |
| 529 | m_cart_slot_config(dynamic_cast<const device_config_cart_slot_interface &>(config)) |
| 530 | { |
| 531 | } |
| 532 | |
| 533 | |
| 534 | //------------------------------------------------- |
| 535 | // ~device_cart_slot_interface - destructor |
| 536 | //------------------------------------------------- |
| 537 | |
| 538 | device_cart_slot_interface::~device_cart_slot_interface() |
| 539 | { |
| 540 | } |
| 541 | |
| 542 | //************************************************************************** |
| 543 | // LEGACY cart_slot DEVICE CONFIGURATION |
| 544 | //************************************************************************** |
| 545 | |
| 546 | //------------------------------------------------- |
| 547 | // legacy_cart_slot_device_config_base - constructor |
| 548 | //------------------------------------------------- |
| 549 | |
| 550 | legacy_cart_slot_device_config_base::legacy_cart_slot_device_config_base(const machine_config &mconfig, device_type type, const char *tag, const device_config *owner, UINT32 clock, device_get_config_func get_config) |
| 551 | : legacy_device_config_base(mconfig, type, tag, owner, clock, get_config), |
| 552 | device_config_cart_slot_interface(mconfig, *this) |
| 553 | { |
| 554 | } |
| 555 | |
| 556 | |
| 557 | //************************************************************************** |
| 558 | // LIVE LEGACY cart_slot DEVICE |
| 559 | //************************************************************************** |
| 560 | |
| 561 | //------------------------------------------------- |
| 562 | // legacy_cart_slot_device_base - constructor |
| 563 | //------------------------------------------------- |
| 564 | |
| 565 | legacy_cart_slot_device_base::legacy_cart_slot_device_base(running_machine &machine, const device_config &config) |
| 566 | : legacy_device_base(machine, config), |
| 567 | device_cart_slot_interface(machine, config, *this) |
| 568 | { |
| 569 | } |
src/emu/imagedev/multcart.c
| r0 | r9928 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | multcart.c |
| 4 | |
| 5 | Multi-cartridge handling code |
| 6 | |
| 7 | *********************************************************************/ |
| 8 | |
| 9 | #include "multcart.h" |
| 10 | #include "pool.h" |
| 11 | #include "unzip.h" |
| 12 | #include "corestr.h" |
| 13 | #include "xmlfile.h" |
| 14 | #include "emu.h" |
| 15 | #include "hash.h" |
| 16 | #include "machine/ram.h" |
| 17 | |
| 18 | /*************************************************************************** |
| 19 | TYPE DEFINITIONS |
| 20 | ***************************************************************************/ |
| 21 | |
| 22 | struct _multicart_private |
| 23 | { |
| 24 | object_pool * pool; |
| 25 | zip_file * zip; |
| 26 | }; |
| 27 | |
| 28 | |
| 29 | typedef struct _multicart_load_state multicart_load_state; |
| 30 | struct _multicart_load_state |
| 31 | { |
| 32 | multicart_t * multicart; |
| 33 | zip_file * zip; |
| 34 | xml_data_node * layout_xml; |
| 35 | xml_data_node * resources_node; |
| 36 | xml_data_node * pcb_node; |
| 37 | multicart_resource * resources; |
| 38 | multicart_socket * sockets; |
| 39 | }; |
| 40 | |
| 41 | static const char error_text[14][30] = |
| 42 | { |
| 43 | "no error", |
| 44 | "not a multicart", |
| 45 | "module definition corrupt", |
| 46 | "out of memory", |
| 47 | "xml format error", |
| 48 | "invalid file reference", |
| 49 | "zip file error", |
| 50 | "missing ram length", |
| 51 | "invalid ram specification", |
| 52 | "unknown resource type", |
| 53 | "invalid resource reference", |
| 54 | "invalid file format", |
| 55 | "missing layout", |
| 56 | "no pcb or resource found" |
| 57 | }; |
| 58 | |
| 59 | const char *multicart_error_text(multicart_open_error error) |
| 60 | { |
| 61 | return error_text[(int)error]; |
| 62 | } |
| 63 | |
| 64 | /*************************************************************************** |
| 65 | IMPLEMENTATION |
| 66 | ***************************************************************************/ |
| 67 | |
| 68 | /*------------------------------------------------- |
| 69 | find_file |
| 70 | -------------------------------------------------*/ |
| 71 | |
| 72 | static const zip_file_header *find_file(zip_file *zip, const char *filename) |
| 73 | { |
| 74 | const zip_file_header *header; |
| 75 | for (header = zip_file_first_file(zip); header != NULL; header = zip_file_next_file(zip)) |
| 76 | { |
| 77 | if (!core_stricmp(header->filename, filename)) |
| 78 | return header; |
| 79 | } |
| 80 | return NULL; |
| 81 | } |
| 82 | |
| 83 | static const zip_file_header *find_file_crc(zip_file *zip, const char *filename, UINT32 crc) |
| 84 | { |
| 85 | const zip_file_header *header; |
| 86 | for (header = zip_file_first_file(zip); header != NULL; header = zip_file_next_file(zip)) |
| 87 | { |
| 88 | // if the CRC and name both match, we're good |
| 89 | // if the CRC matches and the name doesn't, we're still good |
| 90 | if ((!core_stricmp(header->filename, filename)) && (header->crc == crc)) |
| 91 | { |
| 92 | return header; |
| 93 | } |
| 94 | else if (header->crc == crc) |
| 95 | { |
| 96 | return header; |
| 97 | } |
| 98 | } |
| 99 | return NULL; |
| 100 | } |
| 101 | |
| 102 | /*------------------------------------------------- |
| 103 | find_pcb_and_resource_nodes |
| 104 | -------------------------------------------------*/ |
| 105 | |
| 106 | static int find_pcb_and_resource_nodes(xml_data_node *layout_xml, |
| 107 | xml_data_node **pcb_node, xml_data_node **resource_node) |
| 108 | { |
| 109 | xml_data_node *romset_node; |
| 110 | xml_data_node *configuration_node; |
| 111 | |
| 112 | *pcb_node = NULL; |
| 113 | *resource_node = NULL; |
| 114 | |
| 115 | romset_node = xml_get_sibling(layout_xml->child, "romset"); |
| 116 | if (romset_node == NULL) |
| 117 | return FALSE; |
| 118 | |
| 119 | configuration_node = xml_get_sibling(romset_node->child, "configuration"); |
| 120 | if (configuration_node == NULL) |
| 121 | return FALSE; |
| 122 | |
| 123 | *pcb_node = xml_get_sibling(configuration_node->child, "pcb"); |
| 124 | if (*pcb_node == NULL) |
| 125 | return FALSE; |
| 126 | |
| 127 | *resource_node = xml_get_sibling(romset_node->child, "resources"); |
| 128 | if (*resource_node == NULL) |
| 129 | return FALSE; |
| 130 | |
| 131 | return TRUE; |
| 132 | } |
| 133 | |
| 134 | |
| 135 | /*------------------------------------------------- |
| 136 | get_resource_type |
| 137 | -------------------------------------------------*/ |
| 138 | |
| 139 | static multicart_resource_type get_resource_type(const char *s) |
| 140 | { |
| 141 | multicart_resource_type result; |
| 142 | if (!strcmp(s, "rom")) |
| 143 | result = MULTICART_RESOURCE_TYPE_ROM; |
| 144 | else if (!strcmp(s, "ram")) |
| 145 | result = MULTICART_RESOURCE_TYPE_RAM; |
| 146 | else |
| 147 | result = MULTICART_RESOURCE_TYPE_INVALID; |
| 148 | return result; |
| 149 | } |
| 150 | |
| 151 | |
| 152 | /*------------------------------------------------- |
| 153 | load_rom_resource |
| 154 | -------------------------------------------------*/ |
| 155 | |
| 156 | static multicart_open_error load_rom_resource(multicart_load_state *state, xml_data_node *resource_node, |
| 157 | multicart_resource *resource) |
| 158 | { |
| 159 | const char *file, *crcstr, *sha1; |
| 160 | const zip_file_header *header; |
| 161 | zip_error ziperr; |
| 162 | UINT32 crc; |
| 163 | |
| 164 | /* locate the 'file' attribute */ |
| 165 | file = xml_get_attribute_string(resource_node, "file", NULL); |
| 166 | if (file == NULL) |
| 167 | return MCERR_XML_ERROR; |
| 168 | |
| 169 | if (!(crcstr = xml_get_attribute_string(resource_node, "crc", NULL))) |
| 170 | { |
| 171 | /* locate the file in the ZIP file */ |
| 172 | header = find_file(state->zip, file); |
| 173 | } |
| 174 | else /* CRC tag is present, use it */ |
| 175 | { |
| 176 | crc = strtoul(crcstr, NULL, 16); |
| 177 | header = find_file_crc(state->zip, file, crc); |
| 178 | } |
| 179 | if (header == NULL) |
| 180 | return MCERR_INVALID_FILE_REF; |
| 181 | resource->length = header->uncompressed_length; |
| 182 | |
| 183 | /* allocate bytes for this resource */ |
| 184 | resource->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length); |
| 185 | if (resource->ptr == NULL) |
| 186 | return MCERR_OUT_OF_MEMORY; |
| 187 | |
| 188 | /* and decompress it */ |
| 189 | ziperr = zip_file_decompress(state->zip, resource->ptr, resource->length); |
| 190 | if (ziperr != ZIPERR_NONE) |
| 191 | return MCERR_ZIP_ERROR; |
| 192 | |
| 193 | /* check SHA1 now */ |
| 194 | if ((sha1 = xml_get_attribute_string(resource_node, "sha1", NULL))) |
| 195 | { |
| 196 | char calc_sha[256]; |
| 197 | |
| 198 | memset(calc_sha, 0, sizeof(calc_sha)); |
| 199 | hash_compute(&calc_sha[0], (const unsigned char*)resource->ptr, resource->length, HASH_SHA1); |
| 200 | |
| 201 | if ((strncmp(sha1, &calc_sha[2], 20))) |
| 202 | { |
| 203 | return MCERR_INVALID_FILE_REF; |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | return MCERR_NONE; |
| 208 | } |
| 209 | |
| 210 | |
| 211 | /*------------------------------------------------- |
| 212 | load_ram_resource |
| 213 | -------------------------------------------------*/ |
| 214 | |
| 215 | static multicart_open_error load_ram_resource(multicart_load_state *state, xml_data_node *resource_node, |
| 216 | multicart_resource *resource) |
| 217 | { |
| 218 | const char *length_string; |
| 219 | const char *ram_type; |
| 220 | const char *ram_filename; |
| 221 | |
| 222 | astring *ram_pathname; |
| 223 | |
| 224 | /* locate the 'length' attribute */ |
| 225 | length_string = xml_get_attribute_string(resource_node, "length", NULL); |
| 226 | if (length_string == NULL) |
| 227 | return MCERR_MISSING_RAM_LENGTH; |
| 228 | |
| 229 | /* ...and parse it */ |
| 230 | resource->length = ram_parse_string(length_string); |
| 231 | if (resource->length <= 0) |
| 232 | return MCERR_INVALID_RAM_SPEC; |
| 233 | |
| 234 | /* allocate bytes for this resource */ |
| 235 | resource->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length); |
| 236 | if (resource->ptr == NULL) |
| 237 | return MCERR_OUT_OF_MEMORY; |
| 238 | |
| 239 | /* Is this a persistent RAM resource? Then try to load it. */ |
| 240 | ram_type = xml_get_attribute_string(resource_node, "type", NULL); |
| 241 | if (ram_type != NULL) |
| 242 | { |
| 243 | if (strcmp(ram_type, "persistent")==0) |
| 244 | { |
| 245 | astring tmp; |
| 246 | |
| 247 | /* Get the file name. */ |
| 248 | ram_filename = xml_get_attribute_string(resource_node, "file", NULL); |
| 249 | if (ram_filename==NULL) |
| 250 | return MCERR_XML_ERROR; |
| 251 | |
| 252 | ram_pathname = astring_assemble_3(&tmp, state->multicart->gamedrv_name, PATH_SEPARATOR, ram_filename); |
| 253 | |
| 254 | /* Save the file name so that we can write the contents on unloading. |
| 255 | If the RAM resource has no filename, we know that it was volatile only. */ |
| 256 | resource->filename = pool_strdup_lib(state->multicart->data->pool, astring_c(ram_pathname)); |
| 257 | |
| 258 | if (resource->filename == NULL) |
| 259 | return MCERR_OUT_OF_MEMORY; |
| 260 | |
| 261 | image_battery_load_by_name(resource->filename, resource->ptr, resource->length, 0x00); |
| 262 | } |
| 263 | /* else this type is volatile, in which case we just have |
| 264 | a memory expansion */ |
| 265 | } |
| 266 | return MCERR_NONE; |
| 267 | } |
| 268 | |
| 269 | |
| 270 | /*------------------------------------------------- |
| 271 | load_resource |
| 272 | -------------------------------------------------*/ |
| 273 | |
| 274 | static multicart_open_error load_resource(multicart_load_state *state, xml_data_node *resource_node, |
| 275 | multicart_resource_type resource_type) |
| 276 | { |
| 277 | const char *id; |
| 278 | multicart_open_error err; |
| 279 | multicart_resource *resource; |
| 280 | multicart_resource **next_resource; |
| 281 | |
| 282 | /* get the 'id' attribute; error if not present */ |
| 283 | id = xml_get_attribute_string(resource_node, "id", NULL); |
| 284 | if (id == NULL) |
| 285 | return MCERR_XML_ERROR; |
| 286 | |
| 287 | /* allocate memory for the resource */ |
| 288 | resource = (multicart_resource *)pool_malloc_lib(state->multicart->data->pool, sizeof(*resource)); |
| 289 | if (resource == NULL) |
| 290 | return MCERR_OUT_OF_MEMORY; |
| 291 | memset(resource, 0, sizeof(*resource)); |
| 292 | resource->type = resource_type; |
| 293 | |
| 294 | /* copy id */ |
| 295 | resource->id = pool_strdup_lib(state->multicart->data->pool, id); |
| 296 | if (resource->id == NULL) |
| 297 | return MCERR_OUT_OF_MEMORY; |
| 298 | |
| 299 | switch(resource->type) |
| 300 | { |
| 301 | case MULTICART_RESOURCE_TYPE_ROM: |
| 302 | err = load_rom_resource(state, resource_node, resource); |
| 303 | if (err != MCERR_NONE) |
| 304 | return err; |
| 305 | break; |
| 306 | |
| 307 | case MULTICART_RESOURCE_TYPE_RAM: |
| 308 | err = load_ram_resource(state, resource_node, resource); |
| 309 | if (err != MCERR_NONE) |
| 310 | return err; |
| 311 | break; |
| 312 | |
| 313 | default: |
| 314 | return MCERR_UNKNOWN_RESOURCE_TYPE; |
| 315 | } |
| 316 | |
| 317 | /* append the resource */ |
| 318 | for (next_resource = &state->resources; *next_resource; next_resource = &(*next_resource)->next) |
| 319 | ; |
| 320 | *next_resource = resource; |
| 321 | |
| 322 | return MCERR_NONE; |
| 323 | } |
| 324 | |
| 325 | |
| 326 | /*------------------------------------------------- |
| 327 | load_all_resources |
| 328 | -------------------------------------------------*/ |
| 329 | |
| 330 | static multicart_open_error load_all_resources(multicart_load_state *state) |
| 331 | { |
| 332 | multicart_open_error err; |
| 333 | xml_data_node *resource_node; |
| 334 | multicart_resource_type resource_type; |
| 335 | |
| 336 | for (resource_node = state->resources_node->child; resource_node != NULL; resource_node = resource_node->next) |
| 337 | { |
| 338 | resource_type = get_resource_type(resource_node->name); |
| 339 | if (resource_type != MULTICART_RESOURCE_TYPE_INVALID) |
| 340 | { |
| 341 | err = load_resource(state, resource_node, resource_type); |
| 342 | if (err != MCERR_NONE) |
| 343 | return err; |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | state->multicart->resources = state->resources; |
| 348 | return MCERR_NONE; |
| 349 | } |
| 350 | |
| 351 | /*------------------------------------------------- |
| 352 | save_ram_resources. This is important for persistent RAM. All |
| 353 | resources were allocated within the memory pool of this device and will |
| 354 | be freed on multicart_close. |
| 355 | -------------------------------------------------*/ |
| 356 | |
| 357 | static multicart_open_error save_ram_resources(multicart_t *cart) |
| 358 | { |
| 359 | const multicart_resource *resource; |
| 360 | |
| 361 | for (resource = cart->resources; resource != NULL; resource = resource->next) |
| 362 | { |
| 363 | if ((resource->type == MULTICART_RESOURCE_TYPE_RAM) && (resource->filename != NULL)) |
| 364 | { |
| 365 | image_battery_save_by_name(resource->filename, resource->ptr, resource->length); |
| 366 | } |
| 367 | } |
| 368 | return MCERR_NONE; |
| 369 | } |
| 370 | |
| 371 | /*------------------------------------------------- |
| 372 | load_socket |
| 373 | -------------------------------------------------*/ |
| 374 | |
| 375 | static multicart_open_error load_socket(multicart_load_state *state, xml_data_node *socket_node) |
| 376 | { |
| 377 | const char *id; |
| 378 | const char *uses; |
| 379 | const multicart_resource *resource; |
| 380 | multicart_socket *socket; |
| 381 | multicart_socket **next_socket; |
| 382 | |
| 383 | /* get the 'id' and 'uses' attributes; error if not present */ |
| 384 | id = xml_get_attribute_string(socket_node, "id", NULL); |
| 385 | uses = xml_get_attribute_string(socket_node, "uses", NULL); |
| 386 | if ((id == NULL) || (uses == NULL)) |
| 387 | return MCERR_XML_ERROR; |
| 388 | |
| 389 | /* find the resource */ |
| 390 | for (resource = state->multicart->resources; resource != NULL; resource = resource->next) |
| 391 | { |
| 392 | if (!strcmp(uses, resource->id)) |
| 393 | break; |
| 394 | } |
| 395 | if (resource == NULL) |
| 396 | return MCERR_INVALID_RESOURCE_REF; |
| 397 | |
| 398 | /* create the socket */ |
| 399 | socket = (multicart_socket *)pool_malloc_lib(state->multicart->data->pool, sizeof(*socket)); |
| 400 | if (socket == NULL) |
| 401 | return MCERR_OUT_OF_MEMORY; |
| 402 | memset(socket, 0, sizeof(*socket)); |
| 403 | socket->resource = resource; |
| 404 | socket->ptr = resource->ptr; |
| 405 | |
| 406 | /* copy id */ |
| 407 | socket->id = pool_strdup_lib(state->multicart->data->pool, id); |
| 408 | if (socket->id == NULL) |
| 409 | return MCERR_OUT_OF_MEMORY; |
| 410 | |
| 411 | /* which pointer should I use? */ |
| 412 | if (resource->ptr != NULL) |
| 413 | { |
| 414 | /* use the resource's ptr */ |
| 415 | socket->ptr = resource->ptr; |
| 416 | } |
| 417 | else |
| 418 | { |
| 419 | /* allocate bytes for this socket */ |
| 420 | socket->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length); |
| 421 | if (socket->ptr == NULL) |
| 422 | return MCERR_OUT_OF_MEMORY; |
| 423 | |
| 424 | /* ...and clear it */ |
| 425 | memset(socket->ptr, 0xCD, resource->length); |
| 426 | } |
| 427 | |
| 428 | /* append the resource */ |
| 429 | for (next_socket = &state->sockets; *next_socket; next_socket = &(*next_socket)->next) |
| 430 | ; |
| 431 | *next_socket = socket; |
| 432 | |
| 433 | return MCERR_NONE; |
| 434 | } |
| 435 | |
| 436 | |
| 437 | /*------------------------------------------------- |
| 438 | load_all_sockets |
| 439 | -------------------------------------------------*/ |
| 440 | |
| 441 | static multicart_open_error load_all_sockets(multicart_load_state *state) |
| 442 | { |
| 443 | multicart_open_error err; |
| 444 | xml_data_node *socket_node; |
| 445 | |
| 446 | for (socket_node = xml_get_sibling(state->pcb_node->child, "socket"); socket_node != NULL; |
| 447 | socket_node = xml_get_sibling(socket_node->next, "socket")) |
| 448 | { |
| 449 | err = load_socket(state, socket_node); |
| 450 | if (err != MCERR_NONE) |
| 451 | return err; |
| 452 | } |
| 453 | |
| 454 | state->multicart->sockets = state->sockets; |
| 455 | return MCERR_NONE; |
| 456 | } |
| 457 | |
| 458 | |
| 459 | /*------------------------------------------------- |
| 460 | multicart_open - opens a multicart |
| 461 | -------------------------------------------------*/ |
| 462 | |
| 463 | multicart_open_error multicart_open(const char *filename, const char *gamedrv, multicart_load_flags load_flags, multicart_t **cart) |
| 464 | { |
| 465 | multicart_open_error err; |
| 466 | zip_error ziperr; |
| 467 | object_pool *pool; |
| 468 | multicart_load_state state = {0, }; |
| 469 | const zip_file_header *header; |
| 470 | const char *pcb_type; |
| 471 | char *layout_text = NULL; |
| 472 | |
| 473 | /* allocate an object pool */ |
| 474 | pool = pool_alloc_lib(NULL); |
| 475 | if (pool == NULL) |
| 476 | { |
| 477 | err = MCERR_OUT_OF_MEMORY; |
| 478 | goto done; |
| 479 | } |
| 480 | |
| 481 | /* allocate the multicart */ |
| 482 | state.multicart = (multicart_t*)pool_malloc_lib(pool, sizeof(*state.multicart)); |
| 483 | if (state.multicart == NULL) |
| 484 | { |
| 485 | err = MCERR_OUT_OF_MEMORY; |
| 486 | goto done; |
| 487 | } |
| 488 | memset(state.multicart, 0, sizeof(*state.multicart)); |
| 489 | |
| 490 | /* allocate the multicart's private data */ |
| 491 | state.multicart->data = (multicart_private*)pool_malloc_lib(pool, sizeof(*state.multicart->data)); |
| 492 | if (state.multicart->data == NULL) |
| 493 | { |
| 494 | err = MCERR_OUT_OF_MEMORY; |
| 495 | goto done; |
| 496 | } |
| 497 | memset(state.multicart->data, 0, sizeof(*state.multicart->data)); |
| 498 | state.multicart->data->pool = pool; |
| 499 | pool = NULL; |
| 500 | |
| 501 | /* open the ZIP file */ |
| 502 | ziperr = zip_file_open(filename, &state.zip); |
| 503 | if (ziperr != ZIPERR_NONE) |
| 504 | { |
| 505 | err = MCERR_NOT_MULTICART; |
| 506 | goto done; |
| 507 | } |
| 508 | |
| 509 | /* find the layout.xml file */ |
| 510 | header = find_file(state.zip, "layout.xml"); |
| 511 | if (header == NULL) |
| 512 | { |
| 513 | err = MCERR_MISSING_LAYOUT; |
| 514 | goto done; |
| 515 | } |
| 516 | |
| 517 | /* reserve space for the layout text */ |
| 518 | layout_text = (char*)malloc(header->uncompressed_length + 1); |
| 519 | if (layout_text == NULL) |
| 520 | { |
| 521 | err = MCERR_OUT_OF_MEMORY; |
| 522 | goto done; |
| 523 | } |
| 524 | |
| 525 | /* uncompress the layout text */ |
| 526 | ziperr = zip_file_decompress(state.zip, layout_text, header->uncompressed_length); |
| 527 | if (ziperr != ZIPERR_NONE) |
| 528 | { |
| 529 | err = MCERR_ZIP_ERROR; |
| 530 | goto done; |
| 531 | } |
| 532 | layout_text[header->uncompressed_length] = '\0'; |
| 533 | |
| 534 | /* parse the layout text */ |
| 535 | state.layout_xml = xml_string_read(layout_text, NULL); |
| 536 | if (state.layout_xml == NULL) |
| 537 | { |
| 538 | err = MCERR_XML_ERROR; |
| 539 | goto done; |
| 540 | } |
| 541 | |
| 542 | /* locate the PCB node */ |
| 543 | if (!find_pcb_and_resource_nodes(state.layout_xml, &state.pcb_node, &state.resources_node)) |
| 544 | { |
| 545 | err = MCERR_NO_PCB_OR_RESOURCES; |
| 546 | goto done; |
| 547 | } |
| 548 | |
| 549 | /* get the PCB resource_type */ |
| 550 | pcb_type = xml_get_attribute_string(state.pcb_node, "type", ""); |
| 551 | state.multicart->pcb_type = pool_strdup_lib(state.multicart->data->pool, pcb_type); |
| 552 | if (state.multicart->pcb_type == NULL) |
| 553 | { |
| 554 | err = MCERR_OUT_OF_MEMORY; |
| 555 | goto done; |
| 556 | } |
| 557 | |
| 558 | state.multicart->gamedrv_name = pool_strdup_lib(state.multicart->data->pool, gamedrv); |
| 559 | if (state.multicart->gamedrv_name == NULL) |
| 560 | { |
| 561 | err = MCERR_OUT_OF_MEMORY; |
| 562 | goto done; |
| 563 | } |
| 564 | |
| 565 | /* do we have to load resources? */ |
| 566 | if (load_flags & MULTICART_FLAGS_LOAD_RESOURCES) |
| 567 | { |
| 568 | err = load_all_resources(&state); |
| 569 | if (err != MCERR_NONE) |
| 570 | goto done; |
| 571 | |
| 572 | err = load_all_sockets(&state); |
| 573 | if (err != MCERR_NONE) |
| 574 | goto done; |
| 575 | } |
| 576 | |
| 577 | err = MCERR_NONE; |
| 578 | |
| 579 | done: |
| 580 | if (pool != NULL) |
| 581 | pool_free_lib(pool); |
| 582 | if (state.zip != NULL) |
| 583 | zip_file_close(state.zip); |
| 584 | if (layout_text != NULL) |
| 585 | free(layout_text); |
| 586 | if (state.layout_xml != NULL) |
| 587 | xml_file_free(state.layout_xml); |
| 588 | |
| 589 | if ((err != MCERR_NONE) && (state.multicart != NULL)) |
| 590 | { |
| 591 | multicart_close(state.multicart); |
| 592 | state.multicart = NULL; |
| 593 | } |
| 594 | *cart = state.multicart; |
| 595 | return err; |
| 596 | } |
| 597 | |
| 598 | |
| 599 | /*------------------------------------------------- |
| 600 | multicart_close - closes a multicart |
| 601 | -------------------------------------------------*/ |
| 602 | |
| 603 | void multicart_close(multicart_t *cart) |
| 604 | { |
| 605 | save_ram_resources(cart); |
| 606 | pool_free_lib(cart->data->pool); |
| 607 | } |
src/emu/imagedev/snapquik.c
| r0 | r9928 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | snapquik.h |
| 4 | |
| 5 | Snapshots and quickloads |
| 6 | |
| 7 | *********************************************************************/ |
| 8 | |
| 9 | #include "emu.h" |
| 10 | #include "snapquik.h" |
| 11 | |
| 12 | /*************************************************************************** |
| 13 | TYPE DEFINITIONS |
| 14 | ***************************************************************************/ |
| 15 | |
| 16 | typedef struct _snapquick_token snapquick_token; |
| 17 | struct _snapquick_token |
| 18 | { |
| 19 | emu_timer *timer; |
| 20 | snapquick_load_func load; |
| 21 | }; |
| 22 | |
| 23 | |
| 24 | |
| 25 | /*************************************************************************** |
| 26 | INLINE FUNCTIONS |
| 27 | ***************************************************************************/ |
| 28 | |
| 29 | /*------------------------------------------------- |
| 30 | assert_is_snapshot_or_quickload - asserts/confirms |
| 31 | that a given device is a snapshot or quickload |
| 32 | -------------------------------------------------*/ |
| 33 | |
| 34 | INLINE void assert_is_snapshot_or_quickload(device_t *device) |
| 35 | { |
| 36 | assert(device != NULL); |
| 37 | assert(downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config() != NULL); |
| 38 | assert((device->type() == SNAPSHOT) || (device->type() == QUICKLOAD) |
| 39 | || (device->type() == Z80BIN)); |
| 40 | } |
| 41 | |
| 42 | |
| 43 | |
| 44 | /*------------------------------------------------- |
| 45 | get_token - safely gets the snapshot/quickload data |
| 46 | -------------------------------------------------*/ |
| 47 | |
| 48 | INLINE snapquick_token *get_token(device_t *device) |
| 49 | { |
| 50 | assert_is_snapshot_or_quickload(device); |
| 51 | return (snapquick_token *) downcast<legacy_device_base *>(device)->token(); |
| 52 | } |
| 53 | |
| 54 | |
| 55 | |
| 56 | /*------------------------------------------------- |
| 57 | get_config - safely gets the quickload config |
| 58 | -------------------------------------------------*/ |
| 59 | |
| 60 | INLINE const snapquick_config *get_config(device_t *device) |
| 61 | { |
| 62 | assert_is_snapshot_or_quickload(device); |
| 63 | return (const snapquick_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config(); |
| 64 | } |
| 65 | |
| 66 | INLINE const snapquick_config *get_config_dev(const device_config *device) |
| 67 | { |
| 68 | assert(device != NULL); |
| 69 | assert((device->type() == SNAPSHOT) || (device->type() == QUICKLOAD) |
| 70 | || (device->type() == Z80BIN)); |
| 71 | return (const snapquick_config *) downcast<const legacy_device_config_base *>(device)->inline_config(); |
| 72 | } |
| 73 | |
| 74 | /*------------------------------------------------- |
| 75 | log_quickload - logs and displays useful |
| 76 | data for the end user |
| 77 | -------------------------------------------------*/ |
| 78 | |
| 79 | void log_quickload(const char *type, UINT32 start, UINT32 length, UINT32 exec, const char *exec_format) |
| 80 | { |
| 81 | astring tempstring; |
| 82 | |
| 83 | logerror("Loading %04X bytes of RAM at %04X\n", length, start); |
| 84 | |
| 85 | tempstring.catprintf("Quickload type: %s Length: %d bytes\n", type, length); |
| 86 | tempstring.catprintf("Start: 0x%04X End: 0x%04X Exec: ", start, start + length - 1); |
| 87 | |
| 88 | logerror("Quickload loaded.\n"); |
| 89 | if (!mame_stricmp(exec_format, EXEC_NA)) |
| 90 | tempstring.cat("N/A"); |
| 91 | else |
| 92 | { |
| 93 | logerror("Execution can resume with "); |
| 94 | logerror(exec_format, exec); |
| 95 | logerror("\n"); |
| 96 | tempstring.catprintf(exec_format, exec); |
| 97 | } |
| 98 | |
| 99 | ui_popup_time(10, "%s", tempstring.cstr()); |
| 100 | } |
| 101 | |
| 102 | /*************************************************************************** |
| 103 | IMPLEMENTATION |
| 104 | ***************************************************************************/ |
| 105 | |
| 106 | /*------------------------------------------------- |
| 107 | TIMER_CALLBACK(process_snapshot_or_quickload) |
| 108 | -------------------------------------------------*/ |
| 109 | |
| 110 | static TIMER_CALLBACK(process_snapshot_or_quickload) |
| 111 | { |
| 112 | device_image_interface *image = (device_image_interface *) ptr; |
| 113 | snapquick_token *token = get_token(&image->device()); |
| 114 | |
| 115 | /* invoke the load */ |
| 116 | (*token->load)(*image, |
| 117 | image->filetype(), |
| 118 | image->length()); |
| 119 | |
| 120 | /* unload the device */ |
| 121 | image->unload(); |
| 122 | } |
| 123 | |
| 124 | |
| 125 | |
| 126 | /*------------------------------------------------- |
| 127 | DEVICE_START( snapquick ) |
| 128 | -------------------------------------------------*/ |
| 129 | |
| 130 | static DEVICE_START( snapquick ) |
| 131 | { |
| 132 | snapquick_token *token = get_token(device); |
| 133 | |
| 134 | /* allocate a timer */ |
| 135 | token->timer = timer_alloc(device->machine, process_snapshot_or_quickload, (void *) dynamic_cast<device_image_interface *>(device)); |
| 136 | |
| 137 | } |
| 138 | |
| 139 | |
| 140 | |
| 141 | /*------------------------------------------------- |
| 142 | DEVICE_IMAGE_LOAD( snapquick ) |
| 143 | -------------------------------------------------*/ |
| 144 | |
| 145 | static DEVICE_IMAGE_LOAD( snapquick ) |
| 146 | { |
| 147 | const snapquick_config *config = get_config(image); |
| 148 | snapquick_token *token = get_token(image); |
| 149 | |
| 150 | /* locate the load function */ |
| 151 | token->load = (snapquick_load_func) reinterpret_cast<snapquick_load_func>(image.get_device_specific_call()); |
| 152 | |
| 153 | /* adjust the timer */ |
| 154 | timer_adjust_oneshot( |
| 155 | token->timer, |
| 156 | attotime_make(config->delay_seconds, config->delay_attoseconds), |
| 157 | 0); |
| 158 | |
| 159 | return IMAGE_INIT_PASS; |
| 160 | } |
| 161 | |
| 162 | |
| 163 | |
| 164 | /*------------------------------------------------- |
| 165 | DEVICE_GET_INFO(snapquick) - device getinfo |
| 166 | function |
| 167 | -------------------------------------------------*/ |
| 168 | |
| 169 | static DEVICE_GET_INFO(snapquick) |
| 170 | { |
| 171 | switch(state) |
| 172 | { |
| 173 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 174 | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(snapquick_token); break; |
| 175 | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(snapquick_config); break; |
| 176 | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 177 | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 0; break; |
| 178 | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 0; break; |
| 179 | |
| 180 | /* --- the following bits of info are returned as pointers to functions --- */ |
| 181 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(snapquick); break; |
| 182 | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(snapquick); break; |
| 183 | case DEVINFO_FCT_SNAPSHOT_QUICKLOAD_LOAD: info->f = (genf *) get_config_dev(device)->load; break; |
| 184 | |
| 185 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 186 | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 187 | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, get_config_dev(device)->file_extensions); break; |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | |
| 192 | |
| 193 | /*------------------------------------------------- |
| 194 | DEVICE_GET_INFO(snapshot) - device getinfo |
| 195 | function |
| 196 | -------------------------------------------------*/ |
| 197 | |
| 198 | DEVICE_GET_INFO(snapshot) |
| 199 | { |
| 200 | switch(state) |
| 201 | { |
| 202 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 203 | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_SNAPSHOT; break; |
| 204 | |
| 205 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 206 | case DEVINFO_STR_NAME: strcpy(info->s, "Snapshot"); break; |
| 207 | case DEVINFO_STR_FAMILY: strcpy(info->s, "Snapshot"); break; |
| 208 | |
| 209 | default: DEVICE_GET_INFO_CALL(snapquick); break; |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | |
| 214 | |
| 215 | /*------------------------------------------------- |
| 216 | DEVICE_GET_INFO(quickload) - device getinfo |
| 217 | function |
| 218 | -------------------------------------------------*/ |
| 219 | |
| 220 | DEVICE_GET_INFO(quickload) |
| 221 | { |
| 222 | switch(state) |
| 223 | { |
| 224 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 225 | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_QUICKLOAD; break; |
| 226 | |
| 227 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 228 | case DEVINFO_STR_NAME: strcpy(info->s, "Quickload"); break; |
| 229 | case DEVINFO_STR_FAMILY: strcpy(info->s, "Quickload"); break; |
| 230 | |
| 231 | default: DEVICE_GET_INFO_CALL(snapquick); break; |
| 232 | } |
| 233 | } |
| 234 | /*------------------------------------------------- |
| 235 | z80bin_load_file - load a z80bin file into |
| 236 | memory |
| 237 | -------------------------------------------------*/ |
| 238 | |
| 239 | static int z80bin_load_file(device_image_interface *image, const char *file_type, UINT16 *exec_addr, UINT16 *start_addr, UINT16 *end_addr ) |
| 240 | { |
| 241 | int ch; |
| 242 | UINT16 args[3]; |
| 243 | UINT16 i=0, j, size; |
| 244 | UINT8 data; |
| 245 | char pgmname[256]; |
| 246 | char message[256]; |
| 247 | |
| 248 | image->fseek(7, SEEK_SET); |
| 249 | |
| 250 | while((ch = image->fgetc()) != 0x1A) |
| 251 | { |
| 252 | if (ch == EOF) |
| 253 | { |
| 254 | image->seterror(IMAGE_ERROR_INVALIDIMAGE, "Unexpected EOF while getting file name"); |
| 255 | image->message(" Unexpected EOF while getting file name"); |
| 256 | return IMAGE_INIT_FAIL; |
| 257 | } |
| 258 | |
| 259 | if (ch != '\0') |
| 260 | { |
| 261 | if (i >= (ARRAY_LENGTH(pgmname) - 1)) |
| 262 | { |
| 263 | image->seterror(IMAGE_ERROR_INVALIDIMAGE, "File name too long"); |
| 264 | image->message(" File name too long"); |
| 265 | return IMAGE_INIT_FAIL; |
| 266 | } |
| 267 | |
| 268 | pgmname[i] = ch; /* build program name */ |
| 269 | i++; |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | pgmname[i] = '\0'; /* terminate string with a null */ |
| 274 | |
| 275 | if (image->fread(args, sizeof(args)) != sizeof(args)) |
| 276 | { |
| 277 | image->seterror(IMAGE_ERROR_INVALIDIMAGE, "Unexpected EOF while getting file size"); |
| 278 | image->message(" Unexpected EOF while getting file size"); |
| 279 | return IMAGE_INIT_FAIL; |
| 280 | } |
| 281 | |
| 282 | exec_addr[0] = LITTLE_ENDIANIZE_INT16(args[0]); |
| 283 | start_addr[0] = LITTLE_ENDIANIZE_INT16(args[1]); |
| 284 | end_addr[0] = LITTLE_ENDIANIZE_INT16(args[2]); |
| 285 | |
| 286 | size = (end_addr[0] - start_addr[0] + 1) & 0xffff; |
| 287 | |
| 288 | /* display a message about the loaded quickload */ |
| 289 | image->message(" %s\nsize=%04X : start=%04X : end=%04X : exec=%04X",pgmname,size,start_addr[0],end_addr[0],exec_addr[0]); |
| 290 | |
| 291 | for (i = 0; i < size; i++) |
| 292 | { |
| 293 | j = (start_addr[0] + i) & 0xffff; |
| 294 | if (image->fread(&data, 1) != 1) |
| 295 | { |
| 296 | snprintf(message, ARRAY_LENGTH(message), "%s: Unexpected EOF while writing byte to %04X", pgmname, (unsigned) j); |
| 297 | image->seterror(IMAGE_ERROR_INVALIDIMAGE, message); |
| 298 | image->message("%s: Unexpected EOF while writing byte to %04X", pgmname, (unsigned) j); |
| 299 | return IMAGE_INIT_FAIL; |
| 300 | } |
| 301 | cputag_get_address_space(image->device().machine,"maincpu",ADDRESS_SPACE_PROGRAM)->write_byte(j, data); |
| 302 | } |
| 303 | |
| 304 | return IMAGE_INIT_PASS; |
| 305 | } |
| 306 | |
| 307 | |
| 308 | |
| 309 | /*------------------------------------------------- |
| 310 | QUICKLOAD_LOAD( z80bin ) |
| 311 | -------------------------------------------------*/ |
| 312 | |
| 313 | static QUICKLOAD_LOAD( z80bin ) |
| 314 | { |
| 315 | const z80bin_config *config; |
| 316 | UINT16 exec_addr, start_addr, end_addr; |
| 317 | int autorun; |
| 318 | |
| 319 | /* load the binary into memory */ |
| 320 | if (z80bin_load_file(&image, file_type, &exec_addr, &start_addr, &end_addr) == IMAGE_INIT_FAIL) |
| 321 | return IMAGE_INIT_FAIL; |
| 322 | |
| 323 | /* is this file executable? */ |
| 324 | if (exec_addr != 0xffff) |
| 325 | { |
| 326 | config = (const z80bin_config *)downcast<const legacy_device_config_base &>(image.device().baseconfig()).inline_config(); |
| 327 | |
| 328 | /* check to see if autorun is on (I hate how this works) */ |
| 329 | autorun = input_port_read_safe(image.device().machine, "CONFIG", 0xFF) & 1; |
| 330 | |
| 331 | /* start program */ |
| 332 | if (config->execute != NULL) |
| 333 | { |
| 334 | (*config->execute)(image.device().machine, start_addr, end_addr, exec_addr, autorun); |
| 335 | } |
| 336 | else |
| 337 | { |
| 338 | if (autorun) |
| 339 | cpu_set_reg(image.device().machine->device("maincpu"), STATE_GENPC, exec_addr); |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | return IMAGE_INIT_PASS; |
| 344 | } |
| 345 | |
| 346 | |
| 347 | |
| 348 | /*------------------------------------------------- |
| 349 | DEVICE_GET_INFO(z80bin) |
| 350 | -------------------------------------------------*/ |
| 351 | |
| 352 | DEVICE_GET_INFO(z80bin) |
| 353 | { |
| 354 | /* quickload */ |
| 355 | switch(state) |
| 356 | { |
| 357 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 358 | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(z80bin_config); break; |
| 359 | |
| 360 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 361 | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 362 | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, "bin"); break; |
| 363 | |
| 364 | /* --- the following bits of info are returned as pointers to functions --- */ |
| 365 | case DEVINFO_FCT_SNAPSHOT_QUICKLOAD_LOAD: info->f = (genf *) quickload_load_z80bin; break; |
| 366 | |
| 367 | default: DEVICE_GET_INFO_CALL(quickload); break; |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | DEFINE_LEGACY_IMAGE_DEVICE(Z80BIN, z80bin); |
| 372 | DEFINE_LEGACY_IMAGE_DEVICE(SNAPSHOT, snapshot); |
| 373 | DEFINE_LEGACY_IMAGE_DEVICE(QUICKLOAD, quickload); |
src/emu/imagedev/flopimg.c
| r0 | r9928 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | flopimg.c |
| 4 | |
| 5 | Floppy disk image abstraction code |
| 6 | |
| 7 | *********************************************************************/ |
| 8 | |
| 9 | #include <stdlib.h> |
| 10 | #include <string.h> |
| 11 | #include <stdio.h> |
| 12 | #include <ctype.h> |
| 13 | #include <assert.h> |
| 14 | #include <limits.h> |
| 15 | |
| 16 | #include "osdepend.h" |
| 17 | #include "flopimg.h" |
| 18 | #include "pool.h" |
| 19 | #include "imageutl.h" |
| 20 | |
| 21 | #define TRACK_LOADED 0x01 |
| 22 | #define TRACK_DIRTY 0x02 |
| 23 | |
| 24 | |
| 25 | struct _floppy_image |
| 26 | { |
| 27 | struct io_generic io; |
| 28 | |
| 29 | const struct FloppyFormat *floppy_option; |
| 30 | struct FloppyCallbacks format; |
| 31 | |
| 32 | /* loaded track stuff */ |
| 33 | int loaded_track_head; |
| 34 | int loaded_track_index; |
| 35 | UINT32 loaded_track_size; |
| 36 | void *loaded_track_data; |
| 37 | UINT8 loaded_track_status; |
| 38 | UINT8 flags; |
| 39 | |
| 40 | /* tagging system */ |
| 41 | object_pool *tags; |
| 42 | void *tag_data; |
| 43 | }; |
| 44 | |
| 45 | |
| 46 | |
| 47 | struct _floppy_params |
| 48 | { |
| 49 | int param; |
| 50 | int value; |
| 51 | }; |
| 52 | |
| 53 | |
| 54 | |
| 55 | static floperr_t floppy_track_unload(floppy_image *floppy); |
| 56 | |
| 57 | OPTION_GUIDE_START(floppy_option_guide) |
| 58 | OPTION_INT('H', "heads", "Heads") |
| 59 | OPTION_INT('T', "tracks", "Tracks") |
| 60 | OPTION_INT('S', "sectors", "Sectors") |
| 61 | OPTION_INT('L', "sectorlength", "Sector Bytes") |
| 62 | OPTION_INT('I', "interleave", "Interleave") |
| 63 | OPTION_INT('F', "firstsectorid", "First Sector") |
| 64 | OPTION_GUIDE_END |
| 65 | |
| 66 | |
| 67 | static void floppy_close_internal(floppy_image *floppy, int close_file); |
| 68 | |
| 69 | /********************************************************************* |
| 70 | opening, closing and creating of floppy images |
| 71 | *********************************************************************/ |
| 72 | |
| 73 | /* basic floppy_image initialization common to floppy_open() and floppy_create() */ |
| 74 | static floppy_image *floppy_init(void *fp, const struct io_procs *procs, int flags) |
| 75 | { |
| 76 | floppy_image *floppy; |
| 77 | |
| 78 | floppy = (floppy_image *)malloc(sizeof(struct _floppy_image)); |
| 79 | if (!floppy) |
| 80 | return NULL; |
| 81 | |
| 82 | memset(floppy, 0, sizeof(*floppy)); |
| 83 | floppy->tags = pool_alloc_lib(NULL); |
| 84 | floppy->tag_data = NULL; |
| 85 | floppy->io.file = fp; |
| 86 | floppy->io.procs = procs; |
| 87 | floppy->io.filler = 0xFF; |
| 88 | floppy->flags = (UINT8) flags; |
| 89 | return floppy; |
| 90 | } |
| 91 | |
| 92 | |
| 93 | |
| 94 | /* main code for identifying and maybe opening a disk image; not exposed |
| 95 | * directly because this function is big and hideous */ |
| 96 | static floperr_t floppy_open_internal(void *fp, const struct io_procs *procs, const char *extension, |
| 97 | const struct FloppyFormat *floppy_options, int max_options, int flags, floppy_image **outfloppy, |
| 98 | int *outoption) |
| 99 | { |
| 100 | floperr_t err; |
| 101 | floppy_image *floppy; |
| 102 | int best_option = -1; |
| 103 | int best_vote = 0; |
| 104 | int vote; |
| 105 | size_t i; |
| 106 | |
| 107 | floppy = floppy_init(fp, procs, flags); |
| 108 | if (!floppy) |
| 109 | { |
| 110 | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 111 | goto done; |
| 112 | } |
| 113 | |
| 114 | /* vote on the best format */ |
| 115 | for (i = 0; (i < max_options) && floppy_options[i].construct; i++) |
| 116 | { |
| 117 | if (!extension || !floppy_options[i].extensions || image_find_extension(floppy_options[i].extensions, extension)) |
| 118 | { |
| 119 | if (floppy_options[i].identify) |
| 120 | { |
| 121 | vote = 0; |
| 122 | err = floppy_options[i].identify(floppy, &floppy_options[i], &vote); |
| 123 | if (err) |
| 124 | goto done; |
| 125 | } |
| 126 | else |
| 127 | { |
| 128 | vote = 1; |
| 129 | } |
| 130 | |
| 131 | /* is this option a better one? */ |
| 132 | if (vote > best_vote) |
| 133 | { |
| 134 | best_vote = vote; |
| 135 | best_option = i; |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | /* did we find a format? */ |
| 141 | if (best_option == -1) |
| 142 | { |
| 143 | err = FLOPPY_ERROR_INVALIDIMAGE; |
| 144 | goto done; |
| 145 | } |
| 146 | |
| 147 | if (outfloppy) |
| 148 | { |
| 149 | /* call the format constructor */ |
| 150 | err = floppy_options[best_option].construct(floppy, &floppy_options[best_option], NULL); |
| 151 | if (err) |
| 152 | goto done; |
| 153 | |
| 154 | floppy->floppy_option = &floppy_options[best_option]; |
| 155 | } |
| 156 | |
| 157 | err = FLOPPY_ERROR_SUCCESS; |
| 158 | |
| 159 | done: |
| 160 | /* if we have a floppy disk and we either errored or are not keeping it, close it */ |
| 161 | if (floppy && (!outfloppy || err)) |
| 162 | { |
| 163 | floppy_close_internal(floppy, FALSE); |
| 164 | floppy = NULL; |
| 165 | } |
| 166 | |
| 167 | if (outoption) |
| 168 | *outoption = err ? -1 : best_option; |
| 169 | if (outfloppy) |
| 170 | *outfloppy = floppy; |
| 171 | return err; |
| 172 | } |
| 173 | |
| 174 | |
| 175 | |
| 176 | floperr_t floppy_identify(void *fp, const struct io_procs *procs, const char *extension, |
| 177 | const struct FloppyFormat *formats, int *identified_format) |
| 178 | { |
| 179 | return floppy_open_internal(fp, procs, extension, formats, INT_MAX, FLOPPY_FLAGS_READONLY, NULL, identified_format); |
| 180 | } |
| 181 | |
| 182 | |
| 183 | |
| 184 | floperr_t floppy_open(void *fp, const struct io_procs *procs, const char *extension, |
| 185 | const struct FloppyFormat *format, int flags, floppy_image **outfloppy) |
| 186 | { |
| 187 | return floppy_open_internal(fp, procs, extension, format, 1, flags, outfloppy, NULL); |
| 188 | } |
| 189 | |
| 190 | |
| 191 | |
| 192 | floperr_t floppy_open_choices(void *fp, const struct io_procs *procs, const char *extension, |
| 193 | const struct FloppyFormat *formats, int flags, floppy_image **outfloppy) |
| 194 | { |
| 195 | return floppy_open_internal(fp, procs, extension, formats, INT_MAX, flags, outfloppy, NULL); |
| 196 | } |
| 197 | |
| 198 | |
| 199 | |
| 200 | static floperr_t option_to_floppy_error(optreserr_t oerr) |
| 201 | { |
| 202 | floperr_t err; |
| 203 | switch(oerr) { |
| 204 | case OPTIONRESOLUTION_ERROR_SUCCESS: |
| 205 | err = FLOPPY_ERROR_SUCCESS; |
| 206 | break; |
| 207 | case OPTIONRESOLUTION_ERROR_OUTOFMEMORY: |
| 208 | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 209 | break; |
| 210 | case OPTIONRESOLUTION_ERROR_PARAMOUTOFRANGE: |
| 211 | case OPTIONRESOLUTION_ERROR_PARAMNOTSPECIFIED: |
| 212 | case OPTIONRESOLUTION_ERROR_PARAMNOTFOUND: |
| 213 | case OPTIONRESOLUTION_ERROR_PARAMALREADYSPECIFIED: |
| 214 | case OPTIONRESOLUTION_ERROR_BADPARAM: |
| 215 | case OPTIONRESOLUTION_ERROR_SYNTAX: |
| 216 | default: |
| 217 | err = FLOPPY_ERROR_INTERNAL; |
| 218 | break; |
| 219 | }; |
| 220 | return err; |
| 221 | } |
| 222 | |
| 223 | |
| 224 | |
| 225 | floperr_t floppy_create(void *fp, const struct io_procs *procs, const struct FloppyFormat *format, option_resolution *parameters, floppy_image **outfloppy) |
| 226 | { |
| 227 | floppy_image *floppy = NULL; |
| 228 | optreserr_t oerr; |
| 229 | floperr_t err; |
| 230 | int heads, tracks, h, t; |
| 231 | option_resolution *alloc_resolution = NULL; |
| 232 | |
| 233 | assert(format); |
| 234 | |
| 235 | /* create the new image */ |
| 236 | floppy = floppy_init(fp, procs, 0); |
| 237 | if (!floppy) |
| 238 | { |
| 239 | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 240 | goto done; |
| 241 | } |
| 242 | |
| 243 | /* if this format expects creation parameters and none were specified, create some */ |
| 244 | if (!parameters && format->param_guidelines) |
| 245 | { |
| 246 | alloc_resolution = option_resolution_create(floppy_option_guide, format->param_guidelines); |
| 247 | if (!alloc_resolution) |
| 248 | { |
| 249 | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 250 | goto done; |
| 251 | } |
| 252 | parameters = alloc_resolution; |
| 253 | } |
| 254 | |
| 255 | /* finish the parameters, if specified */ |
| 256 | if (parameters) |
| 257 | { |
| 258 | oerr = option_resolution_finish(parameters); |
| 259 | if (oerr) |
| 260 | { |
| 261 | err = option_to_floppy_error(oerr); |
| 262 | goto done; |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | /* call the format constructor */ |
| 267 | err = format->construct(floppy, format, parameters); |
| 268 | if (err) |
| 269 | goto done; |
| 270 | |
| 271 | /* format the disk, ignoring if formatting not implemented */ |
| 272 | if (floppy->format.format_track) |
| 273 | { |
| 274 | heads = floppy_get_heads_per_disk(floppy); |
| 275 | tracks = floppy_get_tracks_per_disk(floppy); |
| 276 | |
| 277 | for (h = 0; h < heads; h++) |
| 278 | { |
| 279 | for (t = 0; t < tracks; t++) |
| 280 | { |
| 281 | err = floppy->format.format_track(floppy, h, t, parameters); |
| 282 | if (err) |
| 283 | goto done; |
| 284 | } |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | /* call the post_format function, if present */ |
| 289 | if (floppy->format.post_format) |
| 290 | { |
| 291 | err = floppy->format.post_format(floppy, parameters); |
| 292 | if (err) |
| 293 | goto done; |
| 294 | } |
| 295 | |
| 296 | floppy->floppy_option = format; |
| 297 | err = FLOPPY_ERROR_SUCCESS; |
| 298 | |
| 299 | done: |
| 300 | if (err && floppy) |
| 301 | { |
| 302 | floppy_close_internal(floppy, FALSE); |
| 303 | floppy = NULL; |
| 304 | } |
| 305 | |
| 306 | if (outfloppy) |
| 307 | *outfloppy = floppy; |
| 308 | else if (floppy) |
| 309 | floppy_close_internal(floppy, FALSE); |
| 310 | |
| 311 | if (alloc_resolution) |
| 312 | option_resolution_close(alloc_resolution); |
| 313 | return err; |
| 314 | } |
| 315 | |
| 316 | |
| 317 | |
| 318 | static void floppy_close_internal(floppy_image *floppy, int close_file) |
| 319 | { |
| 320 | if (floppy) { |
| 321 | floppy_track_unload(floppy); |
| 322 | if (close_file) |
| 323 | io_generic_close(&floppy->io); |
| 324 | if (floppy->loaded_track_data) |
| 325 | free(floppy->loaded_track_data); |
| 326 | pool_free_lib(floppy->tags); |
| 327 | |
| 328 | free(floppy); |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | |
| 333 | |
| 334 | void floppy_close(floppy_image *floppy) |
| 335 | { |
| 336 | floppy_close_internal(floppy, TRUE); |
| 337 | } |
| 338 | |
| 339 | |
| 340 | |
| 341 | /********************************************************************* |
| 342 | functions useful in format constructors |
| 343 | *********************************************************************/ |
| 344 | |
| 345 | struct FloppyCallbacks *floppy_callbacks(floppy_image *floppy) |
| 346 | { |
| 347 | assert(floppy); |
| 348 | return &floppy->format; |
| 349 | } |
| 350 | |
| 351 | |
| 352 | |
| 353 | void *floppy_tag(floppy_image *floppy) |
| 354 | { |
| 355 | assert(floppy); |
| 356 | return floppy->tag_data; |
| 357 | } |
| 358 | |
| 359 | |
| 360 | |
| 361 | void *floppy_create_tag(floppy_image *floppy, size_t tagsize) |
| 362 | { |
| 363 | floppy->tag_data = pool_malloc_lib(floppy->tags,tagsize); |
| 364 | return floppy->tag_data; |
| 365 | } |
| 366 | |
| 367 | |
| 368 | |
| 369 | UINT8 floppy_get_filler(floppy_image *floppy) |
| 370 | { |
| 371 | return floppy->io.filler; |
| 372 | } |
| 373 | |
| 374 | |
| 375 | |
| 376 | void floppy_set_filler(floppy_image *floppy, UINT8 filler) |
| 377 | { |
| 378 | floppy->io.filler = filler; |
| 379 | } |
| 380 | |
| 381 | |
| 382 | |
| 383 | /********************************************************************* |
| 384 | calls for accessing the raw disk image |
| 385 | *********************************************************************/ |
| 386 | |
| 387 | void floppy_image_read(floppy_image *floppy, void *buffer, UINT64 offset, size_t length) |
| 388 | { |
| 389 | io_generic_read(&floppy->io, buffer, offset, length); |
| 390 | } |
| 391 | |
| 392 | |
| 393 | |
| 394 | void floppy_image_write(floppy_image *floppy, const void *buffer, UINT64 offset, size_t length) |
| 395 | { |
| 396 | io_generic_write(&floppy->io, buffer, offset, length); |
| 397 | } |
| 398 | |
| 399 | |
| 400 | |
| 401 | void floppy_image_write_filler(floppy_image *floppy, UINT8 filler, UINT64 offset, size_t length) |
| 402 | { |
| 403 | io_generic_write_filler(&floppy->io, filler, offset, length); |
| 404 | } |
| 405 | |
| 406 | |
| 407 | |
| 408 | UINT64 floppy_image_size(floppy_image *floppy) |
| 409 | { |
| 410 | return io_generic_size(&floppy->io); |
| 411 | } |
| 412 | |
| 413 | |
| 414 | |
| 415 | /********************************************************************* |
| 416 | calls for accessing disk image data |
| 417 | *********************************************************************/ |
| 418 | |
| 419 | static floperr_t floppy_readwrite_sector(floppy_image *floppy, int head, int track, int sector, int offset, |
| 420 | void *buffer, size_t buffer_len, int writing, int indexed, int ddam) |
| 421 | { |
| 422 | floperr_t err; |
| 423 | const struct FloppyCallbacks *fmt; |
| 424 | size_t this_buffer_len; |
| 425 | UINT8 *alloc_buf = NULL; |
| 426 | UINT32 sector_length; |
| 427 | UINT8 *buffer_ptr = (UINT8 *)buffer; |
| 428 | floperr_t (*read_sector)(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen); |
| 429 | floperr_t (*write_sector)(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam); |
| 430 | |
| 431 | fmt = floppy_callbacks(floppy); |
| 432 | |
| 433 | /* choose proper calls for indexed vs non-indexed */ |
| 434 | if (indexed) |
| 435 | { |
| 436 | read_sector = fmt->read_indexed_sector; |
| 437 | write_sector = fmt->write_indexed_sector; |
| 438 | if (!fmt->get_indexed_sector_info) |
| 439 | { |
| 440 | err = FLOPPY_ERROR_UNSUPPORTED; |
| 441 | goto done; |
| 442 | } |
| 443 | } |
| 444 | else |
| 445 | { |
| 446 | read_sector = fmt->read_sector; |
| 447 | write_sector = fmt->write_sector; |
| 448 | if (!fmt->get_sector_length) |
| 449 | { |
| 450 | err = FLOPPY_ERROR_UNSUPPORTED; |
| 451 | goto done; |
| 452 | } |
| 453 | } |
| 454 | |
| 455 | /* check to make sure that the operation is supported */ |
| 456 | if (!read_sector || (writing && !write_sector)) |
| 457 | { |
| 458 | err = FLOPPY_ERROR_UNSUPPORTED; |
| 459 | goto done; |
| 460 | } |
| 461 | |
| 462 | /* main loop */ |
| 463 | while(buffer_len > 0) |
| 464 | { |
| 465 | /* find out the size of this sector */ |
| 466 | if (indexed) |
| 467 | err = fmt->get_indexed_sector_info(floppy, head, track, sector, NULL, NULL, NULL, §or_length, NULL); |
| 468 | else |
| 469 | err = fmt->get_sector_length(floppy, head, track, sector, §or_length); |
| 470 | if (err) |
| 471 | goto done; |
| 472 | |
| 473 | /* do we even do anything with this sector? */ |
| 474 | if (offset < sector_length) |
| 475 | { |
| 476 | /* ok we will be doing something */ |
| 477 | if ((offset > 0) || (buffer_len < sector_length)) |
| 478 | { |
| 479 | /* we will be doing an partial read/write; in other words we |
| 480 | * will not be reading/writing a full sector */ |
| 481 | if (alloc_buf) free(alloc_buf); |
| 482 | alloc_buf = (UINT8*)malloc(sector_length); |
| 483 | if (!alloc_buf) |
| 484 | { |
| 485 | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 486 | goto done; |
| 487 | } |
| 488 | |
| 489 | /* read the sector (we need to do this even when writing */ |
| 490 | err = read_sector(floppy, head, track, sector, alloc_buf, sector_length); |
| 491 | if (err) |
| 492 | goto done; |
| 493 | |
| 494 | this_buffer_len = MIN(buffer_len, sector_length - offset); |
| 495 | |
| 496 | if (writing) |
| 497 | { |
| 498 | memcpy(alloc_buf + offset, buffer_ptr, this_buffer_len); |
| 499 | |
| 500 | err = write_sector(floppy, head, track, sector, alloc_buf, sector_length, ddam); |
| 501 | if (err) |
| 502 | goto done; |
| 503 | } |
| 504 | else |
| 505 | { |
| 506 | memcpy(buffer_ptr, alloc_buf + offset, this_buffer_len); |
| 507 | } |
| 508 | offset += this_buffer_len; |
| 509 | offset %= sector_length; |
| 510 | } |
| 511 | else |
| 512 | { |
| 513 | this_buffer_len = sector_length; |
| 514 | |
| 515 | if (writing) |
| 516 | err = write_sector(floppy, head, track, sector, buffer_ptr, sector_length, ddam); |
| 517 | else |
| 518 | err = read_sector(floppy, head, track, sector, buffer_ptr, sector_length); |
| 519 | if (err) |
| 520 | goto done; |
| 521 | } |
| 522 | } |
| 523 | else |
| 524 | { |
| 525 | /* skip this sector */ |
| 526 | offset -= sector_length; |
| 527 | this_buffer_len = 0; |
| 528 | } |
| 529 | |
| 530 | buffer_ptr += this_buffer_len; |
| 531 | buffer_len -= this_buffer_len; |
| 532 | sector++; |
| 533 | } |
| 534 | |
| 535 | err = FLOPPY_ERROR_SUCCESS; |
| 536 | |
| 537 | done: |
| 538 | if (alloc_buf) |
| 539 | free(alloc_buf); |
| 540 | return err; |
| 541 | } |
| 542 | |
| 543 | |
| 544 | |
| 545 | floperr_t floppy_read_sector(floppy_image *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len) |
| 546 | { |
| 547 | return floppy_readwrite_sector(floppy, head, track, sector, offset, buffer, buffer_len, FALSE, FALSE, 0); |
| 548 | } |
| 549 | |
| 550 | |
| 551 | |
| 552 | floperr_t floppy_write_sector(floppy_image *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam) |
| 553 | { |
| 554 | return floppy_readwrite_sector(floppy, head, track, sector, offset, (void *) buffer, buffer_len, TRUE, FALSE, ddam); |
| 555 | } |
| 556 | |
| 557 | |
| 558 | |
| 559 | floperr_t floppy_read_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len) |
| 560 | { |
| 561 | return floppy_readwrite_sector(floppy, head, track, sector_index, offset, buffer, buffer_len, FALSE, TRUE, 0); |
| 562 | } |
| 563 | |
| 564 | |
| 565 | |
| 566 | floperr_t floppy_write_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam) |
| 567 | { |
| 568 | return floppy_readwrite_sector(floppy, head, track, sector_index, offset, (void *) buffer, buffer_len, TRUE, TRUE, ddam); |
| 569 | } |
| 570 | |
| 571 | |
| 572 | static floperr_t floppy_get_track_data_offset(floppy_image *floppy, int head, int track, UINT64 *offset) |
| 573 | { |
| 574 | floperr_t err; |
| 575 | const struct FloppyCallbacks *callbacks; |
| 576 | |
| 577 | *offset = 0; |
| 578 | callbacks = floppy_callbacks(floppy); |
| 579 | if (callbacks->get_track_data_offset) |
| 580 | { |
| 581 | err = callbacks->get_track_data_offset(floppy, head, track, offset); |
| 582 | if (err) |
| 583 | return err; |
| 584 | } |
| 585 | return FLOPPY_ERROR_SUCCESS; |
| 586 | } |
| 587 | |
| 588 | |
| 589 | |
| 590 | static floperr_t floppy_read_track_offset(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buffer_len) |
| 591 | { |
| 592 | floperr_t err; |
| 593 | const struct FloppyCallbacks *format; |
| 594 | |
| 595 | format = floppy_callbacks(floppy); |
| 596 | |
| 597 | if (!format->read_track) |
| 598 | return FLOPPY_ERROR_UNSUPPORTED; |
| 599 | |
| 600 | err = floppy_track_unload(floppy); |
| 601 | if (err) |
| 602 | return err; |
| 603 | |
| 604 | err = format->read_track(floppy, head, track, offset, buffer, buffer_len); |
| 605 | if (err) |
| 606 | return err; |
| 607 | |
| 608 | return FLOPPY_ERROR_SUCCESS; |
| 609 | } |
| 610 | |
| 611 | |
| 612 | |
| 613 | floperr_t floppy_read_track(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len) |
| 614 | { |
| 615 | return floppy_read_track_offset(floppy, head, track, 0, buffer, buffer_len); |
| 616 | } |
| 617 | |
| 618 | |
| 619 | |
| 620 | floperr_t floppy_read_track_data(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len) |
| 621 | { |
| 622 | floperr_t err; |
| 623 | UINT64 offset; |
| 624 | |
| 625 | err = floppy_get_track_data_offset(floppy, head, track, &offset); |
| 626 | if (err) |
| 627 | return err; |
| 628 | |
| 629 | return floppy_read_track_offset(floppy, head, track, offset, buffer, buffer_len); |
| 630 | } |
| 631 | |
| 632 | |
| 633 | |
| 634 | static floperr_t floppy_write_track_offset(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buffer_len) |
| 635 | { |
| 636 | floperr_t err; |
| 637 | |
| 638 | /* track writing supported? */ |
| 639 | if (!floppy_callbacks(floppy)->write_track) |
| 640 | return FLOPPY_ERROR_UNSUPPORTED; |
| 641 | |
| 642 | /* read only? */ |
| 643 | if (floppy->flags & FLOPPY_FLAGS_READONLY) |
| 644 | return FLOPPY_ERROR_READONLY; |
| 645 | |
| 646 | err = floppy_track_unload(floppy); |
| 647 | if (err) |
| 648 | return err; |
| 649 | |
| 650 | err = floppy_callbacks(floppy)->write_track(floppy, head, track, offset, buffer, buffer_len); |
| 651 | if (err) |
| 652 | return err; |
| 653 | |
| 654 | return FLOPPY_ERROR_SUCCESS; |
| 655 | } |
| 656 | |
| 657 | |
| 658 | |
| 659 | floperr_t floppy_write_track(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len) |
| 660 | { |
| 661 | return floppy_write_track_offset(floppy, head, track, 0, buffer, buffer_len); |
| 662 | } |
| 663 | |
| 664 | |
| 665 | |
| 666 | floperr_t floppy_write_track_data(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len) |
| 667 | { |
| 668 | floperr_t err; |
| 669 | UINT64 offset; |
| 670 | |
| 671 | err = floppy_get_track_data_offset(floppy, head, track, &offset); |
| 672 | if (err) |
| 673 | return err; |
| 674 | |
| 675 | return floppy_write_track_offset(floppy, head, track, offset, buffer, buffer_len); |
| 676 | } |
| 677 | |
| 678 | |
| 679 | |
| 680 | floperr_t floppy_format_track(floppy_image *floppy, int head, int track, option_resolution *parameters) |
| 681 | { |
| 682 | floperr_t err; |
| 683 | struct FloppyCallbacks *format; |
| 684 | option_resolution *alloc_resolution = NULL; |
| 685 | optreserr_t oerr; |
| 686 | |
| 687 | /* supported? */ |
| 688 | format = floppy_callbacks(floppy); |
| 689 | if (!format->format_track) |
| 690 | { |
| 691 | err = FLOPPY_ERROR_UNSUPPORTED; |
| 692 | goto done; |
| 693 | } |
| 694 | |
| 695 | /* create a dummy resolution; if no parameters were specified */ |
| 696 | if (!parameters) |
| 697 | { |
| 698 | alloc_resolution = option_resolution_create(floppy_option_guide, floppy->floppy_option->param_guidelines); |
| 699 | if (!alloc_resolution) |
| 700 | { |
| 701 | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 702 | goto done; |
| 703 | } |
| 704 | parameters = alloc_resolution; |
| 705 | } |
| 706 | |
| 707 | oerr = option_resolution_finish(parameters); |
| 708 | if (oerr) |
| 709 | { |
| 710 | err = option_to_floppy_error(oerr); |
| 711 | goto done; |
| 712 | } |
| 713 | |
| 714 | err = format->format_track(floppy, head, track, parameters); |
| 715 | if (err) |
| 716 | goto done; |
| 717 | |
| 718 | done: |
| 719 | if (alloc_resolution) |
| 720 | option_resolution_close(alloc_resolution); |
| 721 | return err; |
| 722 | } |
| 723 | |
| 724 | |
| 725 | |
| 726 | int floppy_get_tracks_per_disk(floppy_image *floppy) |
| 727 | { |
| 728 | return floppy_callbacks(floppy)->get_tracks_per_disk(floppy); |
| 729 | } |
| 730 | |
| 731 | |
| 732 | |
| 733 | int floppy_get_heads_per_disk(floppy_image *floppy) |
| 734 | { |
| 735 | return floppy_callbacks(floppy)->get_heads_per_disk(floppy); |
| 736 | } |
| 737 | |
| 738 | |
| 739 | |
| 740 | UINT32 floppy_get_track_size(floppy_image *floppy, int head, int track) |
| 741 | { |
| 742 | const struct FloppyCallbacks *fmt; |
| 743 | |
| 744 | fmt = floppy_callbacks(floppy); |
| 745 | if (!fmt->get_track_size) |
| 746 | return 0; |
| 747 | |
| 748 | return fmt->get_track_size(floppy, head, track); |
| 749 | } |
| 750 | |
| 751 | |
| 752 | |
| 753 | floperr_t floppy_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) |
| 754 | { |
| 755 | const struct FloppyCallbacks *fmt; |
| 756 | |
| 757 | fmt = floppy_callbacks(floppy); |
| 758 | if (!fmt->get_sector_length) |
| 759 | return FLOPPY_ERROR_UNSUPPORTED; |
| 760 | |
| 761 | return fmt->get_sector_length(floppy, head, track, sector, sector_length); |
| 762 | } |
| 763 | |
| 764 | |
| 765 | |
| 766 | floperr_t floppy_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) |
| 767 | { |
| 768 | const struct FloppyCallbacks *fmt; |
| 769 | |
| 770 | fmt = floppy_callbacks(floppy); |
| 771 | if (!fmt->get_indexed_sector_info) |
| 772 | return FLOPPY_ERROR_UNSUPPORTED; |
| 773 | |
| 774 | return fmt->get_indexed_sector_info(floppy, head, track, sector_index, cylinder, side, sector, sector_length, flags); |
| 775 | } |
| 776 | |
| 777 | |
| 778 | |
| 779 | floperr_t floppy_get_sector_count(floppy_image *floppy, int head, int track, int *sector_count) |
| 780 | { |
| 781 | floperr_t err; |
| 782 | int sector_index = 0; |
| 783 | |
| 784 | do |
| 785 | { |
| 786 | err = floppy_get_indexed_sector_info(floppy, head, track, sector_index, NULL, NULL, NULL, NULL, NULL); |
| 787 | if (!err) |
| 788 | sector_index++; |
| 789 | } |
| 790 | while(!err); |
| 791 | |
| 792 | if (sector_index && (err == FLOPPY_ERROR_SEEKERROR)) |
| 793 | err = FLOPPY_ERROR_SUCCESS; |
| 794 | if (sector_count) |
| 795 | *sector_count = err ? 0 : sector_index; |
| 796 | return err; |
| 797 | } |
| 798 | |
| 799 | |
| 800 | |
| 801 | int floppy_is_read_only(floppy_image *floppy) |
| 802 | { |
| 803 | return floppy->flags & FLOPPY_FLAGS_READONLY; |
| 804 | } |
| 805 | |
| 806 | |
| 807 | |
| 808 | UINT8 floppy_random_byte(floppy_image *floppy) |
| 809 | { |
| 810 | /* can't use mame_rand(); this might not be in the core */ |
| 811 | #ifdef rand |
| 812 | #undef rand |
| 813 | #endif |
| 814 | return rand(); |
| 815 | } |
| 816 | |
| 817 | |
| 818 | |
| 819 | /********************************************************************* |
| 820 | calls for track based IO |
| 821 | *********************************************************************/ |
| 822 | |
| 823 | floperr_t floppy_load_track(floppy_image *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length) |
| 824 | { |
| 825 | floperr_t err; |
| 826 | void *new_loaded_track_data; |
| 827 | UINT32 track_size; |
| 828 | |
| 829 | /* have we already loaded this track? */ |
| 830 | if (((floppy->loaded_track_status & TRACK_LOADED) == 0) || (head != floppy->loaded_track_head) || (track != floppy->loaded_track_index)) |
| 831 | { |
| 832 | err = floppy_track_unload(floppy); |
| 833 | if (err) |
| 834 | goto error; |
| 835 | |
| 836 | track_size = floppy_callbacks(floppy)->get_track_size(floppy, head, track); |
| 837 | |
| 838 | if (floppy->loaded_track_data) free(floppy->loaded_track_data); |
| 839 | new_loaded_track_data = malloc(track_size); |
| 840 | if (!new_loaded_track_data) |
| 841 | { |
| 842 | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 843 | goto error; |
| 844 | } |
| 845 | |
| 846 | floppy->loaded_track_data = new_loaded_track_data; |
| 847 | floppy->loaded_track_size = track_size; |
| 848 | floppy->loaded_track_head = head; |
| 849 | floppy->loaded_track_index = track; |
| 850 | |
| 851 | err = floppy_callbacks(floppy)->read_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data, floppy->loaded_track_size); |
| 852 | if (err) |
| 853 | goto error; |
| 854 | |
| 855 | floppy->loaded_track_status |= TRACK_LOADED | (dirtify ? TRACK_DIRTY : 0); |
| 856 | } |
| 857 | else |
| 858 | floppy->loaded_track_status |= (dirtify ? TRACK_DIRTY : 0); |
| 859 | |
| 860 | if (track_data) |
| 861 | *track_data = floppy->loaded_track_data; |
| 862 | if (track_length) |
| 863 | *track_length = floppy->loaded_track_size; |
| 864 | return FLOPPY_ERROR_SUCCESS; |
| 865 | |
| 866 | error: |
| 867 | if (track_data) |
| 868 | *track_data = NULL; |
| 869 | if (track_length) |
| 870 | *track_length = 0; |
| 871 | return err; |
| 872 | } |
| 873 | |
| 874 | |
| 875 | |
| 876 | static floperr_t floppy_track_unload(floppy_image *floppy) |
| 877 | { |
| 878 | int err; |
| 879 | if (floppy->loaded_track_status & TRACK_DIRTY) |
| 880 | { |
| 881 | err = floppy_callbacks(floppy)->write_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data, floppy->loaded_track_size); |
| 882 | if (err) |
| 883 | return (floperr_t)err; |
| 884 | } |
| 885 | |
| 886 | floppy->loaded_track_status &= ~(TRACK_LOADED | TRACK_DIRTY); |
| 887 | return FLOPPY_ERROR_SUCCESS; |
| 888 | } |
| 889 | |
| 890 | |
| 891 | |
| 892 | /********************************************************************* |
| 893 | accessors for meta information about the image |
| 894 | *********************************************************************/ |
| 895 | |
| 896 | const char *floppy_format_description(floppy_image *floppy) |
| 897 | { |
| 898 | return floppy->floppy_option->description; |
| 899 | } |
| 900 | |
| 901 | |
| 902 | |
| 903 | /********************************************************************* |
| 904 | misc calls |
| 905 | *********************************************************************/ |
| 906 | |
| 907 | const char *floppy_error(floperr_t err) |
| 908 | { |
| 909 | static const char *const error_messages[] = |
| 910 | { |
| 911 | "The operation completed successfully", |
| 912 | "Fatal internal error", |
| 913 | "This operation is unsupported", |
| 914 | "Out of memory", |
| 915 | "Seek error", |
| 916 | "Invalid image", |
| 917 | "Attempted to write to read only image", |
| 918 | "No space left on image", |
| 919 | "Parameter out of range", |
| 920 | "Required parameter not specified" |
| 921 | }; |
| 922 | |
| 923 | if ((err < 0) || (err >= ARRAY_LENGTH(error_messages))) |
| 924 | return NULL; |
| 925 | return error_messages[err]; |
| 926 | } |
| 927 | |
| 928 | |
| 929 | FLOPPY_OPTIONS_START(default) |
| 930 | FLOPPY_OPTIONS_END |
src/emu/imagedev/wavfile.c
| r0 | r9928 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | wavfile.c |
| 4 | |
| 5 | Format code for wave (*.wav) files |
| 6 | |
| 7 | *********************************************************************/ |
| 8 | |
| 9 | #include <stdio.h> |
| 10 | #include <assert.h> |
| 11 | |
| 12 | #include "wavfile.h" |
| 13 | #include "cassimg.h" |
| 14 | |
| 15 | static const char magic1[4] = { 'R', 'I', 'F', 'F' }; |
| 16 | static const char magic2[4] = { 'W', 'A', 'V', 'E' }; |
| 17 | static const char format_tag_id[4] = { 'f', 'm', 't', ' ' }; |
| 18 | static const char data_tag_id[4] = { 'd', 'a', 't', 'a' }; |
| 19 | |
| 20 | #define WAV_FORMAT_PCM 1 |
| 21 | |
| 22 | |
| 23 | |
| 24 | static UINT32 get_leuint32(const void *ptr) |
| 25 | { |
| 26 | UINT32 value; |
| 27 | memcpy(&value, ptr, sizeof(value)); |
| 28 | return LITTLE_ENDIANIZE_INT32(value); |
| 29 | } |
| 30 | |
| 31 | |
| 32 | |
| 33 | static UINT16 get_leuint16(const void *ptr) |
| 34 | { |
| 35 | UINT16 value; |
| 36 | memcpy(&value, ptr, sizeof(value)); |
| 37 | return LITTLE_ENDIANIZE_INT16(value); |
| 38 | } |
| 39 | |
| 40 | |
| 41 | |
| 42 | static void put_leuint32(void *ptr, UINT32 value) |
| 43 | { |
| 44 | value = LITTLE_ENDIANIZE_INT32(value); |
| 45 | memcpy(ptr, &value, sizeof(value)); |
| 46 | } |
| 47 | |
| 48 | |
| 49 | |
| 50 | static void put_leuint16(void *ptr, UINT16 value) |
| 51 | { |
| 52 | value = LITTLE_ENDIANIZE_INT16(value); |
| 53 | memcpy(ptr, &value, sizeof(value)); |
| 54 | } |
| 55 | |
| 56 | |
| 57 | |
| 58 | static casserr_t wavfile_process(cassette_image *cassette, struct CassetteOptions *opts, |
| 59 | int read_waveform) |
| 60 | { |
| 61 | UINT8 file_header[12]; |
| 62 | UINT8 tag_header[8]; |
| 63 | UINT8 format_tag[16]; |
| 64 | UINT32 stated_size; |
| 65 | UINT64 file_size; |
| 66 | UINT32 tag_size; |
| 67 | UINT32 tag_samples; |
| 68 | UINT64 offset; |
| 69 | int format_specified = FALSE; |
| 70 | |
| 71 | UINT16 format_type = 0; |
| 72 | UINT32 bytes_per_second = 0; |
| 73 | UINT16 block_align = 0; |
| 74 | int waveform_flags = 0; |
| 75 | |
| 76 | /* read header */ |
| 77 | cassette_image_read(cassette, file_header, 0, sizeof(file_header)); |
| 78 | offset = sizeof(file_header); |
| 79 | |
| 80 | /* check magic numbers */ |
| 81 | if (memcmp(&file_header[0], magic1, 4)) |
| 82 | return CASSETTE_ERROR_INVALIDIMAGE; |
| 83 | if (memcmp(&file_header[8], magic2, 4)) |
| 84 | return CASSETTE_ERROR_INVALIDIMAGE; |
| 85 | |
| 86 | /* read and sanity check size */ |
| 87 | stated_size = get_leuint32(&file_header[4]) + 8; |
| 88 | file_size = cassette_image_size(cassette); |
| 89 | if (stated_size > file_size) |
| 90 | stated_size = (UINT32) file_size; |
| 91 | |
| 92 | while(offset < stated_size) |
| 93 | { |
| 94 | cassette_image_read(cassette, tag_header, offset, sizeof(tag_header)); |
| 95 | tag_size = get_leuint32(&tag_header[4]); |
| 96 | offset += sizeof(tag_header); |
| 97 | |
| 98 | if (!memcmp(tag_header, format_tag_id, 4)) |
| 99 | { |
| 100 | /* format tag */ |
| 101 | if (format_specified || (tag_size < sizeof(format_tag))) |
| 102 | return CASSETTE_ERROR_INVALIDIMAGE; |
| 103 | format_specified = TRUE; |
| 104 | |
| 105 | cassette_image_read(cassette, format_tag, offset, sizeof(format_tag)); |
| 106 | |
| 107 | format_type = get_leuint16(&format_tag[0]); |
| 108 | opts->channels = get_leuint16(&format_tag[2]); |
| 109 | opts->sample_frequency = get_leuint32(&format_tag[4]); |
| 110 | bytes_per_second = get_leuint32(&format_tag[8]); |
| 111 | block_align = get_leuint16(&format_tag[12]); |
| 112 | opts->bits_per_sample = get_leuint16(&format_tag[14]); |
| 113 | |
| 114 | if (format_type != WAV_FORMAT_PCM) |
| 115 | return CASSETTE_ERROR_INVALIDIMAGE; |
| 116 | if (opts->sample_frequency * opts->bits_per_sample * opts->channels / 8 != bytes_per_second) |
| 117 | return CASSETTE_ERROR_INVALIDIMAGE; |
| 118 | |
| 119 | switch(opts->bits_per_sample) |
| 120 | { |
| 121 | case 8: |
| 122 | waveform_flags = CASSETTE_WAVEFORM_8BIT; |
| 123 | break; |
| 124 | case 16: |
| 125 | waveform_flags = CASSETTE_WAVEFORM_16BITLE; |
| 126 | break; |
| 127 | case 32: |
| 128 | waveform_flags = CASSETTE_WAVEFORM_32BITLE; |
| 129 | break; |
| 130 | default: |
| 131 | return CASSETTE_ERROR_INVALIDIMAGE; |
| 132 | } |
| 133 | } |
| 134 | else if (!memcmp(tag_header, data_tag_id, 4)) |
| 135 | { |
| 136 | /* data tag */ |
| 137 | if (!format_specified) |
| 138 | return CASSETTE_ERROR_INVALIDIMAGE; |
| 139 | |
| 140 | if (read_waveform) |
| 141 | { |
| 142 | tag_samples = tag_size / (opts->bits_per_sample / 8) / opts->channels; |
| 143 | cassette_read_samples(cassette, opts->channels, 0.0, tag_samples / ((double) opts->sample_frequency), |
| 144 | tag_samples, offset, waveform_flags); |
| 145 | } |
| 146 | } |
| 147 | else |
| 148 | { |
| 149 | /* ignore other tags */ |
| 150 | } |
| 151 | offset += tag_size; |
| 152 | } |
| 153 | |
| 154 | return CASSETTE_ERROR_SUCCESS; |
| 155 | } |
| 156 | |
| 157 | |
| 158 | |
| 159 | static casserr_t wavfile_identify(cassette_image *cassette, struct CassetteOptions *opts) |
| 160 | { |
| 161 | return wavfile_process(cassette, opts, FALSE); |
| 162 | } |
| 163 | |
| 164 | |
| 165 | |
| 166 | static casserr_t wavfile_load(cassette_image *cassette) |
| 167 | { |
| 168 | struct CassetteOptions opts; |
| 169 | memset(&opts, 0, sizeof(opts)); |
| 170 | return wavfile_process(cassette, &opts, TRUE); |
| 171 | } |
| 172 | |
| 173 | |
| 174 | |
| 175 | static casserr_t wavfile_save(cassette_image *cassette, const struct CassetteInfo *info) |
| 176 | { |
| 177 | casserr_t err; |
| 178 | UINT8 consolidated_header[12 + 8 + 16 + 8]; |
| 179 | UINT8 *header = &consolidated_header[0]; |
| 180 | UINT8 *format_tag_header = &consolidated_header[12]; |
| 181 | UINT8 *format_tag_data = &consolidated_header[12 + 8]; |
| 182 | UINT8 *data_tag_header = &consolidated_header[12 + 8 + 16]; |
| 183 | UINT32 file_size; |
| 184 | UINT32 bytes_per_second; |
| 185 | UINT16 bits_per_sample; |
| 186 | UINT32 data_size; |
| 187 | size_t bytes_per_sample = 2; |
| 188 | int waveform_flags = CASSETTE_WAVEFORM_16BITLE; |
| 189 | UINT16 block_align; |
| 190 | |
| 191 | bits_per_sample = (UINT16) (bytes_per_sample * 8); |
| 192 | bytes_per_second = info->sample_frequency * bytes_per_sample * info->channels; |
| 193 | data_size = (UINT32) (info->sample_count * bytes_per_sample * info->channels); |
| 194 | file_size = data_size + sizeof(consolidated_header) - 8; |
| 195 | block_align = (UINT16) (bytes_per_sample * info->channels); |
| 196 | |
| 197 | /* set up header */ |
| 198 | memcpy(&header[0], magic1, 4); |
| 199 | memcpy(&header[8], magic2, 4); |
| 200 | put_leuint32(&header[4], file_size); |
| 201 | |
| 202 | /* set up format tag */ |
| 203 | memcpy(&format_tag_header[0], format_tag_id, 4); |
| 204 | put_leuint32(&format_tag_header[4], 16); |
| 205 | put_leuint16(&format_tag_data[0], WAV_FORMAT_PCM); |
| 206 | put_leuint16(&format_tag_data[2], info->channels); |
| 207 | put_leuint32(&format_tag_data[4], info->sample_frequency); |
| 208 | put_leuint32(&format_tag_data[8], bytes_per_second); |
| 209 | put_leuint16(&format_tag_data[12], block_align); |
| 210 | put_leuint16(&format_tag_data[14], bits_per_sample); |
| 211 | |
| 212 | /* set up data tag */ |
| 213 | memcpy(&data_tag_header[0], data_tag_id, 4); |
| 214 | put_leuint32(&data_tag_header[4], data_size); |
| 215 | |
| 216 | /* write consolidated header */ |
| 217 | cassette_image_write(cassette, consolidated_header, 0, sizeof(consolidated_header)); |
| 218 | |
| 219 | /* write out the actual data */ |
| 220 | err = cassette_write_samples(cassette, info->channels, 0.0, info->sample_count |
| 221 | / (double) info->sample_frequency, info->sample_count, sizeof(consolidated_header), |
| 222 | waveform_flags); |
| 223 | if (err) |
| 224 | return err; |
| 225 | |
| 226 | return CASSETTE_ERROR_SUCCESS; |
| 227 | } |
| 228 | |
| 229 | |
| 230 | |
| 231 | const struct CassetteFormat wavfile_format = |
| 232 | { |
| 233 | "wav", |
| 234 | wavfile_identify, |
| 235 | wavfile_load, |
| 236 | wavfile_save |
| 237 | }; |
| 238 | |
| 239 | |
| 240 | |
| 241 | /********************************************************************* |
| 242 | wavfile_testload() |
| 243 | |
| 244 | This is a hokey function used to test the cassette wave loading |
| 245 | system, specifically to test that when one loads a WAV file image |
| 246 | that the resulting info queried will be the same data in the WAV. |
| 247 | |
| 248 | This code has already identified some rounding errors |
| 249 | *********************************************************************/ |
| 250 | |
| 251 | #ifdef UNUSED_FUNCTION |
| 252 | void wavfile_testload(const char *fname) |
| 253 | { |
| 254 | cassette_image *cassette; |
| 255 | FILE *f; |
| 256 | long offset; |
| 257 | int freq, samples, i; |
| 258 | INT32 cassamp; |
| 259 | INT16 wavsamp; |
| 260 | |
| 261 | f = fopen(fname, "rb"); |
| 262 | if (!f) |
| 263 | return; |
| 264 | |
| 265 | if (cassette_open(f, &stdio_ioprocs, &wavfile_format, CASSETTE_FLAG_READONLY, &cassette)) |
| 266 | { |
| 267 | fclose(f); |
| 268 | return; |
| 269 | } |
| 270 | |
| 271 | offset = 44; |
| 272 | freq = 44100; |
| 273 | samples = 5667062; |
| 274 | |
| 275 | for (i = 0; i < samples; i++) |
| 276 | { |
| 277 | cassette_get_sample(cassette, 0, i / (double) freq, 0.0, &cassamp); |
| 278 | |
| 279 | fseek(f, offset + i * 2, SEEK_SET); |
| 280 | fread(&wavsamp, 1, 2, f); |
| 281 | assert(cassamp == (((UINT32) wavsamp) << 16)); |
| 282 | } |
| 283 | |
| 284 | cassette_close(cassette); |
| 285 | |
| 286 | fclose(f); |
| 287 | } |
| 288 | #endif |
| 289 | |
| 290 | |
| 291 | |
src/mess/uimess.c
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | uimess.c |
| 4 | | |
| 5 | | MESS supplement to ui.c. |
| 6 | | |
| 7 | | *********************************************************************/ |
| 8 | | |
| 9 | | #include "emu.h" |
| 10 | | #include "uimenu.h" |
| 11 | | #include "uimess.h" |
| 12 | | #include "uiinput.h" |
| 13 | | #include "input.h" |
| 14 | | #include "devices/cassette.h" |
| 15 | | #include "devices/bitbngr.h" |
| 16 | | |
| 17 | | |
| 18 | | /*************************************************************************** |
| 19 | | TYPE DEFINITIONS |
| 20 | | ***************************************************************************/ |
| 21 | | #define TAPECMD_NULL ((void *) 0x0000) |
| 22 | | #define TAPECMD_STOP ((void *) 0x0001) |
| 23 | | #define TAPECMD_PLAY ((void *) 0x0002) |
| 24 | | #define TAPECMD_RECORD ((void *) 0x0003) |
| 25 | | #define TAPECMD_REWIND ((void *) 0x0004) |
| 26 | | #define TAPECMD_FAST_FORWARD ((void *) 0x0005) |
| 27 | | #define TAPECMD_SLIDER ((void *) 0x0006) |
| 28 | | #define TAPECMD_SELECT ((void *) 0x0007) |
| 29 | | |
| 30 | | #define BITBANGERCMD_SELECT ((void *) 0x0000) |
| 31 | | #define BITBANGERCMD_MODE ((void *) 0x0001) |
| 32 | | #define BITBANGERCMD_BAUD ((void *) 0x0002) |
| 33 | | #define BITBANGERCMD_TUNE ((void *) 0x0003) |
| 34 | | |
| 35 | | |
| 36 | | typedef struct _tape_control_menu_state tape_control_menu_state; |
| 37 | | struct _tape_control_menu_state |
| 38 | | { |
| 39 | | int index; |
| 40 | | device_image_interface *device; |
| 41 | | }; |
| 42 | | |
| 43 | | |
| 44 | | |
| 45 | | typedef struct _bitbanger_control_menu_state bitbanger_control_menu_state; |
| 46 | | struct _bitbanger_control_menu_state |
| 47 | | { |
| 48 | | int index; |
| 49 | | device_image_interface *device; |
| 50 | | }; |
| 51 | | |
| 52 | | |
| 53 | | |
| 54 | | /*************************************************************************** |
| 55 | | IMPLEMENTATION |
| 56 | | ***************************************************************************/ |
| 57 | | |
| 58 | | /*------------------------------------------------- |
| 59 | | ui_mess_main_menu_populate - populate MESS-specific menus |
| 60 | | -------------------------------------------------*/ |
| 61 | | |
| 62 | | void ui_mess_main_menu_populate(running_machine *machine, ui_menu *menu) |
| 63 | | { |
| 64 | | /* add tape control menu */ |
| 65 | | if (machine->m_devicelist.first(CASSETTE)) |
| 66 | | ui_menu_item_append(menu, "Tape Control", NULL, 0, (void*)ui_mess_menu_tape_control); |
| 67 | | |
| 68 | | /* add bitbanger control menu */ |
| 69 | | if (machine->m_devicelist.first(BITBANGER)) |
| 70 | | ui_menu_item_append(menu, "Bitbanger Control", NULL, 0, (void*)ui_mess_menu_bitbanger_control); |
| 71 | | } |
| 72 | | |
| 73 | | |
| 74 | | /*------------------------------------------------- |
| 75 | | cassette_count - returns the number of cassette |
| 76 | | devices in the machine |
| 77 | | -------------------------------------------------*/ |
| 78 | | |
| 79 | | INLINE int cassette_count( running_machine *machine ) |
| 80 | | { |
| 81 | | int count = 0; |
| 82 | | device_t *device = machine->m_devicelist.first(CASSETTE); |
| 83 | | |
| 84 | | while ( device ) |
| 85 | | { |
| 86 | | count++; |
| 87 | | device = device->typenext(); |
| 88 | | } |
| 89 | | return count; |
| 90 | | } |
| 91 | | |
| 92 | | /*------------------------------------------------- |
| 93 | | bitbanger_count - returns the number of bitbanger |
| 94 | | devices in the machine |
| 95 | | -------------------------------------------------*/ |
| 96 | | |
| 97 | | INLINE int bitbanger_count( running_machine *machine ) |
| 98 | | { |
| 99 | | int count = 0; |
| 100 | | device_t *device = machine->m_devicelist.first(BITBANGER); |
| 101 | | |
| 102 | | while ( device ) |
| 103 | | { |
| 104 | | count++; |
| 105 | | device = device->typenext(); |
| 106 | | } |
| 107 | | return count; |
| 108 | | } |
| 109 | | |
| 110 | | /*------------------------------------------------- |
| 111 | | tapecontrol_gettime - returns a textual |
| 112 | | representation of the time |
| 113 | | -------------------------------------------------*/ |
| 114 | | |
| 115 | | astring *tapecontrol_gettime(astring *dest, device_t *device, int *curpos, int *endpos) |
| 116 | | { |
| 117 | | double t0, t1; |
| 118 | | |
| 119 | | t0 = cassette_get_position(device); |
| 120 | | t1 = cassette_get_length(device); |
| 121 | | |
| 122 | | if (t1) |
| 123 | | astring_printf(dest, "%04d/%04d", (int) t0, (int) t1); |
| 124 | | else |
| 125 | | astring_printf(dest, "%04d/%04d", 0, (int) t1); |
| 126 | | |
| 127 | | if (curpos != NULL) |
| 128 | | *curpos = t0; |
| 129 | | if (endpos != NULL) |
| 130 | | *endpos = t1; |
| 131 | | |
| 132 | | return dest; |
| 133 | | } |
| 134 | | |
| 135 | | |
| 136 | | |
| 137 | | /*------------------------------------------------- |
| 138 | | menu_tape_control_populate - populates the |
| 139 | | main tape control menu |
| 140 | | -------------------------------------------------*/ |
| 141 | | |
| 142 | | static void menu_tape_control_populate(running_machine *machine, ui_menu *menu, tape_control_menu_state *menustate) |
| 143 | | { |
| 144 | | astring timepos; |
| 145 | | cassette_state state; |
| 146 | | int count = cassette_count(machine); |
| 147 | | UINT32 flags = 0; |
| 148 | | |
| 149 | | if( count > 0 ) |
| 150 | | { |
| 151 | | if( menustate->index == (count-1) ) |
| 152 | | flags |= MENU_FLAG_LEFT_ARROW; |
| 153 | | else |
| 154 | | flags |= MENU_FLAG_RIGHT_ARROW; |
| 155 | | } |
| 156 | | |
| 157 | | if (menustate->device->exists()) |
| 158 | | { |
| 159 | | double t0, t1; |
| 160 | | UINT32 tapeflags = 0; |
| 161 | | |
| 162 | | t0 = cassette_get_position(&menustate->device->device()); |
| 163 | | t1 = cassette_get_length(&menustate->device->device()); |
| 164 | | |
| 165 | | if (t1 > 0) |
| 166 | | { |
| 167 | | if (t0 > 0) |
| 168 | | tapeflags |= MENU_FLAG_LEFT_ARROW; |
| 169 | | if (t0 < t1) |
| 170 | | tapeflags |= MENU_FLAG_RIGHT_ARROW; |
| 171 | | } |
| 172 | | |
| 173 | | /* name of tape */ |
| 174 | | ui_menu_item_append(menu, menustate->device->image_config().devconfig().name(), menustate->device->filename(), flags, TAPECMD_SELECT); |
| 175 | | |
| 176 | | /* state */ |
| 177 | | tapecontrol_gettime(&timepos, &menustate->device->device(), NULL, NULL); |
| 178 | | state = cassette_get_state(&menustate->device->device()); |
| 179 | | ui_menu_item_append( |
| 180 | | menu, |
| 181 | | (state & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED |
| 182 | | ? "stopped" |
| 183 | | : ((state & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY |
| 184 | | ? ((state & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED ? "playing" : "(playing)") |
| 185 | | : ((state & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED ? "recording" : "(recording)") |
| 186 | | ), |
| 187 | | astring_c(&timepos), |
| 188 | | tapeflags, |
| 189 | | TAPECMD_SLIDER); |
| 190 | | |
| 191 | | /* pause or stop */ |
| 192 | | ui_menu_item_append(menu, "Pause/Stop", NULL, 0, TAPECMD_STOP); |
| 193 | | |
| 194 | | /* play */ |
| 195 | | ui_menu_item_append(menu, "Play", NULL, 0, TAPECMD_PLAY); |
| 196 | | |
| 197 | | /* record */ |
| 198 | | ui_menu_item_append(menu, "Record", NULL, 0, TAPECMD_RECORD); |
| 199 | | |
| 200 | | /* rewind */ |
| 201 | | ui_menu_item_append(menu, "Rewind", NULL, 0, TAPECMD_REWIND); |
| 202 | | |
| 203 | | /* fast forward */ |
| 204 | | ui_menu_item_append(menu, "Fast Forward", NULL, 0, TAPECMD_FAST_FORWARD); |
| 205 | | } |
| 206 | | else |
| 207 | | { |
| 208 | | /* no tape loaded */ |
| 209 | | ui_menu_item_append(menu, "No Tape Image loaded", NULL, flags, NULL); |
| 210 | | } |
| 211 | | } |
| 212 | | |
| 213 | | |
| 214 | | /*------------------------------------------------- |
| 215 | | menu_bitbanger_control_populate - populates the |
| 216 | | main bitbanger control menu |
| 217 | | -------------------------------------------------*/ |
| 218 | | |
| 219 | | static void menu_bitbanger_control_populate(running_machine *machine, ui_menu *menu, bitbanger_control_menu_state *menustate) |
| 220 | | { |
| 221 | | int count = bitbanger_count(machine); |
| 222 | | UINT32 flags = 0, mode_flags = 0, baud_flags = 0, tune_flags = 0; |
| 223 | | |
| 224 | | if( count > 0 ) |
| 225 | | { |
| 226 | | if( menustate->index == (count-1) ) |
| 227 | | flags |= MENU_FLAG_LEFT_ARROW; |
| 228 | | else |
| 229 | | flags |= MENU_FLAG_RIGHT_ARROW; |
| 230 | | } |
| 231 | | |
| 232 | | if (bitbanger_inc_mode(&menustate->device->device(), TRUE)) |
| 233 | | mode_flags |= MENU_FLAG_RIGHT_ARROW; |
| 234 | | |
| 235 | | if (bitbanger_dec_mode(&menustate->device->device(), TRUE)) |
| 236 | | mode_flags |= MENU_FLAG_LEFT_ARROW; |
| 237 | | |
| 238 | | if (bitbanger_inc_baud(&menustate->device->device(), TRUE)) |
| 239 | | baud_flags |= MENU_FLAG_RIGHT_ARROW; |
| 240 | | |
| 241 | | if (bitbanger_dec_baud(&menustate->device->device(), TRUE)) |
| 242 | | baud_flags |= MENU_FLAG_LEFT_ARROW; |
| 243 | | |
| 244 | | if (bitbanger_inc_tune(&menustate->device->device(), TRUE)) |
| 245 | | tune_flags |= MENU_FLAG_RIGHT_ARROW; |
| 246 | | |
| 247 | | if (bitbanger_dec_tune(&menustate->device->device(), TRUE)) |
| 248 | | tune_flags |= MENU_FLAG_LEFT_ARROW; |
| 249 | | |
| 250 | | |
| 251 | | if (menustate->device->exists()) |
| 252 | | { |
| 253 | | /* name of bitbanger file */ |
| 254 | | ui_menu_item_append(menu, menustate->device->image_config().devconfig().name(), menustate->device->filename(), flags, BITBANGERCMD_SELECT); |
| 255 | | ui_menu_item_append(menu, "Device Mode:", bitbanger_mode_string(&menustate->device->device()), mode_flags, BITBANGERCMD_MODE); |
| 256 | | ui_menu_item_append(menu, "Baud:", bitbanger_baud_string(&menustate->device->device()), baud_flags, BITBANGERCMD_BAUD); |
| 257 | | ui_menu_item_append(menu, "Baud Tune:", bitbanger_tune_string(&menustate->device->device()), tune_flags, BITBANGERCMD_TUNE); |
| 258 | | ui_menu_item_append(menu, "Protocol:", "8-1-N", 0, NULL); |
| 259 | | } |
| 260 | | else |
| 261 | | { |
| 262 | | /* no tape loaded */ |
| 263 | | ui_menu_item_append(menu, "No Bitbanger Image loaded", NULL, flags, NULL); |
| 264 | | } |
| 265 | | } |
| 266 | | |
| 267 | | |
| 268 | | /*------------------------------------------------- |
| 269 | | menu_tape_control - main tape control menu |
| 270 | | -------------------------------------------------*/ |
| 271 | | |
| 272 | | void ui_mess_menu_tape_control(running_machine *machine, ui_menu *menu, void *parameter, void *state) |
| 273 | | { |
| 274 | | tape_control_menu_state *menustate; |
| 275 | | const ui_menu_event *event; |
| 276 | | |
| 277 | | /* if no state, allocate some */ |
| 278 | | if (state == NULL) |
| 279 | | state = ui_menu_alloc_state(menu, sizeof(*menustate), NULL); |
| 280 | | menustate = (tape_control_menu_state *) state; |
| 281 | | |
| 282 | | /* do we have to load the device? */ |
| 283 | | if (menustate->device == NULL) |
| 284 | | { |
| 285 | | int index = menustate->index; |
| 286 | | device_image_interface *device = NULL; |
| 287 | | for (bool gotone = machine->m_devicelist.first(device); gotone; gotone = device->next(device)) |
| 288 | | { |
| 289 | | if(device->device().type() == CASSETTE) { |
| 290 | | if (index==0) break; |
| 291 | | index--; |
| 292 | | } |
| 293 | | } |
| 294 | | menustate->device = device; |
| 295 | | ui_menu_reset(menu, (ui_menu_reset_options)0); |
| 296 | | } |
| 297 | | |
| 298 | | /* rebuild the menu - we have to do this so that the counter updates */ |
| 299 | | ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION); |
| 300 | | menu_tape_control_populate(machine, menu, (tape_control_menu_state*)state); |
| 301 | | |
| 302 | | /* process the menu */ |
| 303 | | event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT); |
| 304 | | if (event != NULL) |
| 305 | | { |
| 306 | | switch(event->iptkey) |
| 307 | | { |
| 308 | | case IPT_UI_LEFT: |
| 309 | | if (event->itemref==TAPECMD_SLIDER) |
| 310 | | cassette_seek(&menustate->device->device(), -1, SEEK_CUR); |
| 311 | | else |
| 312 | | if (event->itemref==TAPECMD_SELECT) |
| 313 | | { |
| 314 | | /* left arrow - rotate left through cassette devices */ |
| 315 | | if (menustate->index > 0) |
| 316 | | menustate->index--; |
| 317 | | else |
| 318 | | menustate->index = cassette_count(machine) - 1; |
| 319 | | menustate->device = NULL; |
| 320 | | } |
| 321 | | break; |
| 322 | | |
| 323 | | case IPT_UI_RIGHT: |
| 324 | | if (event->itemref==TAPECMD_SLIDER) |
| 325 | | cassette_seek(&menustate->device->device(), +1, SEEK_CUR); |
| 326 | | else |
| 327 | | if (event->itemref==TAPECMD_SELECT) |
| 328 | | { |
| 329 | | /* right arrow - rotate right through cassette devices */ |
| 330 | | if (menustate->index < cassette_count(machine) - 1) |
| 331 | | menustate->index++; |
| 332 | | else |
| 333 | | menustate->index = 0; |
| 334 | | menustate->device = NULL; |
| 335 | | } |
| 336 | | break; |
| 337 | | |
| 338 | | case IPT_UI_SELECT: |
| 339 | | { |
| 340 | | if (event->itemref==TAPECMD_STOP) |
| 341 | | cassette_change_state(&menustate->device->device(), CASSETTE_STOPPED, CASSETTE_MASK_UISTATE); |
| 342 | | else |
| 343 | | if (event->itemref==TAPECMD_PLAY) |
| 344 | | cassette_change_state(&menustate->device->device(), CASSETTE_PLAY, CASSETTE_MASK_UISTATE); |
| 345 | | else |
| 346 | | if (event->itemref==TAPECMD_RECORD) |
| 347 | | cassette_change_state(&menustate->device->device(), CASSETTE_RECORD, CASSETTE_MASK_UISTATE); |
| 348 | | else |
| 349 | | if (event->itemref==TAPECMD_REWIND) |
| 350 | | cassette_seek(&menustate->device->device(), -30, SEEK_CUR); |
| 351 | | else |
| 352 | | if (event->itemref==TAPECMD_FAST_FORWARD) |
| 353 | | cassette_seek(&menustate->device->device(), 30, SEEK_CUR); |
| 354 | | else |
| 355 | | if (event->itemref==TAPECMD_SLIDER) |
| 356 | | cassette_seek(&menustate->device->device(), 0, SEEK_SET); |
| 357 | | } |
| 358 | | break; |
| 359 | | } |
| 360 | | } |
| 361 | | } |
| 362 | | |
| 363 | | |
| 364 | | /*------------------------------------------------- |
| 365 | | menu_bitbanger_control - main bitbanger |
| 366 | | control menu |
| 367 | | -------------------------------------------------*/ |
| 368 | | |
| 369 | | void ui_mess_menu_bitbanger_control(running_machine *machine, ui_menu *menu, void *parameter, void *state) |
| 370 | | { |
| 371 | | bitbanger_control_menu_state *menustate; |
| 372 | | const ui_menu_event *event; |
| 373 | | |
| 374 | | /* if no state, allocate some */ |
| 375 | | if (state == NULL) |
| 376 | | state = ui_menu_alloc_state(menu, sizeof(*menustate), NULL); |
| 377 | | menustate = (bitbanger_control_menu_state *) state; |
| 378 | | |
| 379 | | /* do we have to load the device? */ |
| 380 | | if (menustate->device == NULL) |
| 381 | | { |
| 382 | | int index = menustate->index; |
| 383 | | device_image_interface *device = NULL; |
| 384 | | for (bool gotone = machine->m_devicelist.first(device); gotone; gotone = device->next(device)) |
| 385 | | { |
| 386 | | if(device->device().type() == BITBANGER) { |
| 387 | | if (index==0) break; |
| 388 | | index--; |
| 389 | | } |
| 390 | | } |
| 391 | | menustate->device = device; |
| 392 | | ui_menu_reset(menu, (ui_menu_reset_options)0); |
| 393 | | } |
| 394 | | |
| 395 | | /* rebuild the menu */ |
| 396 | | ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION); |
| 397 | | menu_bitbanger_control_populate(machine, menu, (bitbanger_control_menu_state*)state); |
| 398 | | |
| 399 | | /* process the menu */ |
| 400 | | event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT); |
| 401 | | if (event != NULL) |
| 402 | | { |
| 403 | | switch(event->iptkey) |
| 404 | | { |
| 405 | | case IPT_UI_LEFT: |
| 406 | | if (event->itemref==BITBANGERCMD_SELECT) |
| 407 | | { |
| 408 | | /* left arrow - rotate left through cassette devices */ |
| 409 | | if (menustate->index > 0) |
| 410 | | menustate->index--; |
| 411 | | else |
| 412 | | menustate->index = bitbanger_count(machine) - 1; |
| 413 | | menustate->device = NULL; |
| 414 | | } |
| 415 | | else if (event->itemref==BITBANGERCMD_MODE) |
| 416 | | { |
| 417 | | bitbanger_dec_mode(&menustate->device->device(), FALSE); |
| 418 | | } |
| 419 | | else if (event->itemref==BITBANGERCMD_BAUD) |
| 420 | | { |
| 421 | | bitbanger_dec_baud(&menustate->device->device(), FALSE); |
| 422 | | } |
| 423 | | else if (event->itemref==BITBANGERCMD_TUNE) |
| 424 | | { |
| 425 | | bitbanger_dec_tune(&menustate->device->device(), FALSE); |
| 426 | | } |
| 427 | | break; |
| 428 | | |
| 429 | | case IPT_UI_RIGHT: |
| 430 | | if (event->itemref==BITBANGERCMD_SELECT) |
| 431 | | { |
| 432 | | /* right arrow - rotate right through cassette devices */ |
| 433 | | if (menustate->index < bitbanger_count(machine) - 1) |
| 434 | | menustate->index++; |
| 435 | | else |
| 436 | | menustate->index = 0; |
| 437 | | menustate->device = NULL; |
| 438 | | } |
| 439 | | else if (event->itemref==BITBANGERCMD_MODE) |
| 440 | | { |
| 441 | | bitbanger_inc_mode(&menustate->device->device(), FALSE); |
| 442 | | } |
| 443 | | else if (event->itemref==BITBANGERCMD_BAUD) |
| 444 | | { |
| 445 | | bitbanger_inc_baud(&menustate->device->device(), FALSE); |
| 446 | | } |
| 447 | | else if (event->itemref==BITBANGERCMD_TUNE) |
| 448 | | { |
| 449 | | bitbanger_inc_tune(&menustate->device->device(), FALSE); |
| 450 | | } |
| 451 | | break; |
| 452 | | } |
| 453 | | } |
| 454 | | } |
| 455 | | |
src/mess/devices/harddriv.c
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | Code to interface the MESS image code with MAME's harddisk core. |
| 4 | | |
| 5 | | We do not support diff files as it will involve some changes in |
| 6 | | the MESS image code. Additionally, the need for diff files comes |
| 7 | | from MAME's need for "cannonical" hard drive images. |
| 8 | | |
| 9 | | Raphael Nabet 2003 |
| 10 | | |
| 11 | | Update: 23-Feb-2004 - Unlike floppy disks, for which we support |
| 12 | | myriad formats on many systems, it is my intention for MESS to |
| 13 | | standardize on the CHD file format for hard drives so I made a few |
| 14 | | changes to support this |
| 15 | | |
| 16 | | *********************************************************************/ |
| 17 | | |
| 18 | | #include "emu.h" |
| 19 | | #include "harddisk.h" |
| 20 | | #include "harddriv.h" |
| 21 | | |
| 22 | | |
| 23 | | static const char *const error_strings[] = |
| 24 | | { |
| 25 | | "no error", |
| 26 | | "no drive interface", |
| 27 | | "out of memory", |
| 28 | | "invalid file", |
| 29 | | "invalid parameter", |
| 30 | | "invalid data", |
| 31 | | "file not found", |
| 32 | | "requires parent", |
| 33 | | "file not writeable", |
| 34 | | "read error", |
| 35 | | "write error", |
| 36 | | "codec error", |
| 37 | | "invalid parent", |
| 38 | | "hunk out of range", |
| 39 | | "decompression error", |
| 40 | | "compression error", |
| 41 | | "can't create file", |
| 42 | | "can't verify file" |
| 43 | | "operation not supported", |
| 44 | | "can't find metadata", |
| 45 | | "invalid metadata size", |
| 46 | | "unsupported CHD version" |
| 47 | | }; |
| 48 | | |
| 49 | | static const char *chd_get_error_string(int chderr) |
| 50 | | { |
| 51 | | if ((chderr < 0 ) || (chderr >= ARRAY_LENGTH(error_strings))) |
| 52 | | return NULL; |
| 53 | | return error_strings[chderr]; |
| 54 | | } |
| 55 | | |
| 56 | | |
| 57 | | |
| 58 | | static OPTION_GUIDE_START(mess_hd_option_guide) |
| 59 | | OPTION_INT('C', "cylinders", "Cylinders") |
| 60 | | OPTION_INT('H', "heads", "Heads") |
| 61 | | OPTION_INT('S', "sectors", "Sectors") |
| 62 | | OPTION_INT('L', "sectorlength", "Sector Bytes") |
| 63 | | OPTION_INT('K', "hunksize", "Hunk Bytes") |
| 64 | | OPTION_GUIDE_END |
| 65 | | |
| 66 | | static const char *mess_hd_option_spec = |
| 67 | | "C1-[512]-1024;H1/2/[4]/8;S1-[16]-64;L128/256/[512]/1024;K512/1024/2048/[4096]"; |
| 68 | | |
| 69 | | |
| 70 | | typedef struct _dev_harddisk_t dev_harddisk_t; |
| 71 | | struct _dev_harddisk_t |
| 72 | | { |
| 73 | | const struct harddisk_callback_config *config; |
| 74 | | chd_file *chd; |
| 75 | | hard_disk_file *hard_disk_handle; |
| 76 | | }; |
| 77 | | |
| 78 | | |
| 79 | | INLINE dev_harddisk_t *get_safe_token(device_t *device) |
| 80 | | { |
| 81 | | assert( device != NULL ); |
| 82 | | assert( ( device->type() == HARDDISK ) || |
| 83 | | ( device->type() == IDE_HARDDISK ) ); |
| 84 | | return (dev_harddisk_t *) downcast<legacy_device_base *>(device)->token(); |
| 85 | | } |
| 86 | | |
| 87 | | |
| 88 | | /************************************* |
| 89 | | * |
| 90 | | * DEVICE_IMAGE_LOAD(mess_hd) |
| 91 | | * DEVICE_IMAGE_CREATE(mess_hd) |
| 92 | | * |
| 93 | | * Device load and create |
| 94 | | * |
| 95 | | *************************************/ |
| 96 | | |
| 97 | | static int internal_load_mess_hd(device_image_interface &image, const char *metadata) |
| 98 | | { |
| 99 | | dev_harddisk_t *harddisk = get_safe_token( &image.device() ); |
| 100 | | chd_error err = (chd_error)0; |
| 101 | | int is_writeable; |
| 102 | | |
| 103 | | /* open the CHD file */ |
| 104 | | do |
| 105 | | { |
| 106 | | is_writeable = image.is_writable(); |
| 107 | | harddisk->chd = NULL; |
| 108 | | err = chd_open_file(image.image_core_file(), is_writeable ? CHD_OPEN_READWRITE : CHD_OPEN_READ, NULL, &harddisk->chd); |
| 109 | | |
| 110 | | /* special case; if we get CHDERR_FILE_NOT_WRITEABLE, make the |
| 111 | | * image read only and repeat */ |
| 112 | | if (err == CHDERR_FILE_NOT_WRITEABLE) |
| 113 | | image.make_readonly(); |
| 114 | | } |
| 115 | | while(!harddisk->chd && is_writeable && (err == CHDERR_FILE_NOT_WRITEABLE)); |
| 116 | | if (!harddisk->chd) |
| 117 | | goto done; |
| 118 | | |
| 119 | | /* if we created the image and hence, have metadata to set, set the metadata */ |
| 120 | | if (metadata) |
| 121 | | { |
| 122 | | err = chd_set_metadata(harddisk->chd, HARD_DISK_METADATA_TAG, 0, metadata, strlen(metadata) + 1, 0); |
| 123 | | if (err != CHDERR_NONE) |
| 124 | | goto done; |
| 125 | | } |
| 126 | | |
| 127 | | /* open the hard disk file */ |
| 128 | | harddisk->hard_disk_handle = hard_disk_open(harddisk->chd); |
| 129 | | if (!harddisk->hard_disk_handle) |
| 130 | | goto done; |
| 131 | | |
| 132 | | done: |
| 133 | | if (err) |
| 134 | | { |
| 135 | | /* if we had an error, close out the CHD */ |
| 136 | | if (harddisk->chd != NULL) |
| 137 | | { |
| 138 | | chd_close(harddisk->chd); |
| 139 | | harddisk->chd = NULL; |
| 140 | | } |
| 141 | | |
| 142 | | image.seterror(IMAGE_ERROR_UNSPECIFIED, chd_get_error_string(err)); |
| 143 | | } |
| 144 | | return err ? IMAGE_INIT_FAIL : IMAGE_INIT_PASS; |
| 145 | | } |
| 146 | | |
| 147 | | |
| 148 | | |
| 149 | | static DEVICE_IMAGE_LOAD( mess_hd ) |
| 150 | | { |
| 151 | | dev_harddisk_t *harddisk = get_safe_token( image ); |
| 152 | | int our_result; |
| 153 | | |
| 154 | | our_result = internal_load_mess_hd(image, NULL); |
| 155 | | |
| 156 | | /* Check if there is an image_load callback defined */ |
| 157 | | if ( harddisk->config && harddisk->config->device_image_load ) |
| 158 | | { |
| 159 | | /* Let the override do some additional work/checks */ |
| 160 | | our_result = harddisk->config->device_image_load( image ); |
| 161 | | } |
| 162 | | return our_result; |
| 163 | | } |
| 164 | | |
| 165 | | |
| 166 | | static DEVICE_IMAGE_CREATE( mess_hd ) |
| 167 | | { |
| 168 | | int err; |
| 169 | | char metadata[256]; |
| 170 | | UINT32 sectorsize, hunksize; |
| 171 | | UINT32 cylinders, heads, sectors, totalsectors; |
| 172 | | |
| 173 | | cylinders = option_resolution_lookup_int(create_args, 'C'); |
| 174 | | heads = option_resolution_lookup_int(create_args, 'H'); |
| 175 | | sectors = option_resolution_lookup_int(create_args, 'S'); |
| 176 | | sectorsize = option_resolution_lookup_int(create_args, 'L'); |
| 177 | | hunksize = option_resolution_lookup_int(create_args, 'K'); |
| 178 | | |
| 179 | | totalsectors = cylinders * heads * sectors; |
| 180 | | |
| 181 | | /* create the CHD file */ |
| 182 | | err = chd_create_file(image.image_core_file(), (UINT64)totalsectors * (UINT64)sectorsize, hunksize, CHDCOMPRESSION_NONE, NULL); |
| 183 | | if (err != CHDERR_NONE) |
| 184 | | goto error; |
| 185 | | |
| 186 | | sprintf(metadata, HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, sectorsize); |
| 187 | | return internal_load_mess_hd(image, metadata); |
| 188 | | |
| 189 | | error: |
| 190 | | return IMAGE_INIT_FAIL; |
| 191 | | } |
| 192 | | |
| 193 | | |
| 194 | | |
| 195 | | /************************************* |
| 196 | | * |
| 197 | | * DEVICE_IMAGE_UNLOAD(mess_hd) |
| 198 | | * |
| 199 | | * Device unload |
| 200 | | * |
| 201 | | *************************************/ |
| 202 | | |
| 203 | | static DEVICE_IMAGE_UNLOAD( mess_hd ) |
| 204 | | { |
| 205 | | dev_harddisk_t *harddisk = get_safe_token( image ); |
| 206 | | |
| 207 | | /* Check if there is an image_unload callback defined */ |
| 208 | | if ( harddisk->config && harddisk->config->device_image_unload ) |
| 209 | | { |
| 210 | | harddisk->config->device_image_unload( image ); |
| 211 | | } |
| 212 | | |
| 213 | | if (harddisk->hard_disk_handle != NULL) |
| 214 | | { |
| 215 | | hard_disk_close(harddisk->hard_disk_handle); |
| 216 | | harddisk->hard_disk_handle = NULL; |
| 217 | | } |
| 218 | | |
| 219 | | if (harddisk->chd != NULL) |
| 220 | | { |
| 221 | | chd_close(harddisk->chd); |
| 222 | | harddisk->chd = NULL; |
| 223 | | } |
| 224 | | } |
| 225 | | |
| 226 | | |
| 227 | | /************************************* |
| 228 | | * |
| 229 | | * Get the MESS/MAME hard disk handle (from the src/harddisk.c core) |
| 230 | | * after an image has been opened with the mess_hd core |
| 231 | | * |
| 232 | | *************************************/ |
| 233 | | |
| 234 | | hard_disk_file *mess_hd_get_hard_disk_file(device_t *device) |
| 235 | | { |
| 236 | | dev_harddisk_t *harddisk = get_safe_token( device ); |
| 237 | | |
| 238 | | return harddisk->hard_disk_handle; |
| 239 | | } |
| 240 | | |
| 241 | | |
| 242 | | /************************************* |
| 243 | | * |
| 244 | | * Get the MESS/MAME CHD file (from the src/chd.c core) |
| 245 | | * after an image has been opened with the mess_hd core |
| 246 | | * |
| 247 | | *************************************/ |
| 248 | | |
| 249 | | chd_file *mess_hd_get_chd_file(device_t *device) |
| 250 | | { |
| 251 | | chd_file *result = NULL; |
| 252 | | hard_disk_file *hd_file; |
| 253 | | |
| 254 | | if (device) |
| 255 | | { |
| 256 | | hd_file = mess_hd_get_hard_disk_file(device); |
| 257 | | if (hd_file) |
| 258 | | result = hard_disk_get_chd(hd_file); |
| 259 | | } |
| 260 | | return result; |
| 261 | | } |
| 262 | | |
| 263 | | |
| 264 | | /*------------------------------------------------- |
| 265 | | DEVICE_START(mess_hd) |
| 266 | | -------------------------------------------------*/ |
| 267 | | |
| 268 | | static DEVICE_START(mess_hd) |
| 269 | | { |
| 270 | | dev_harddisk_t *harddisk = get_safe_token( device ); |
| 271 | | |
| 272 | | harddisk->config = (const harddisk_callback_config*)device->baseconfig().static_config(); |
| 273 | | harddisk->chd = NULL; |
| 274 | | harddisk->hard_disk_handle = NULL; |
| 275 | | } |
| 276 | | |
| 277 | | |
| 278 | | /*------------------------------------------------- |
| 279 | | DEVICE_GET_INFO(mess_hd) |
| 280 | | -------------------------------------------------*/ |
| 281 | | |
| 282 | | DEVICE_GET_INFO(mess_hd) |
| 283 | | { |
| 284 | | switch( state ) |
| 285 | | { |
| 286 | | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 287 | | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(dev_harddisk_t); break; |
| 288 | | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; |
| 289 | | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_HARDDISK; break; |
| 290 | | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 291 | | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break; |
| 292 | | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 0; break; |
| 293 | | case DEVINFO_INT_IMAGE_CREATE_OPTCOUNT: info->i = 1; break; |
| 294 | | |
| 295 | | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 296 | | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(mess_hd); break; |
| 297 | | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(mess_hd); break; |
| 298 | | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(mess_hd); break; |
| 299 | | case DEVINFO_FCT_IMAGE_CREATE: info->f = (genf *) DEVICE_IMAGE_CREATE_NAME(mess_hd); break; |
| 300 | | case DEVINFO_PTR_IMAGE_CREATE_OPTGUIDE: info->p = (void *) mess_hd_option_guide; break; |
| 301 | | case DEVINFO_PTR_IMAGE_CREATE_OPTSPEC+0: info->p = (void *) mess_hd_option_spec; break; |
| 302 | | |
| 303 | | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 304 | | case DEVINFO_STR_NAME: strcpy(info->s, "Harddisk"); break; |
| 305 | | case DEVINFO_STR_FAMILY: strcpy(info->s, "Harddisk"); break; |
| 306 | | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 307 | | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, "chd,hd"); break; |
| 308 | | case DEVINFO_STR_IMAGE_CREATE_OPTNAME+0: strcpy(info->s, "chd"); break; |
| 309 | | case DEVINFO_STR_IMAGE_CREATE_OPTDESC+0: strcpy(info->s, "MAME/MESS CHD Hard drive"); break; |
| 310 | | case DEVINFO_STR_IMAGE_CREATE_OPTEXTS+0: strcpy(info->s, "chd,hd"); break; |
| 311 | | } |
| 312 | | } |
| 313 | | |
| 314 | | |
| 315 | | static DEVICE_START(mess_ide) |
| 316 | | { |
| 317 | | /* old code from idedrive.c */ |
| 318 | | #ifdef UNUSED_FUNCTION |
| 319 | | int which_bus, which_address; |
| 320 | | struct ide_interface *intf; |
| 321 | | device_start_func parent_init; |
| 322 | | |
| 323 | | /* get the basics */ |
| 324 | | ide_get_params(device, &which_bus, &which_address, &intf, &parent_init, NULL, NULL); |
| 325 | | |
| 326 | | /* call the parent init function */ |
| 327 | | parent_init(device); |
| 328 | | |
| 329 | | /* configure IDE */ |
| 330 | | /* FIXME IDE */ |
| 331 | | /* ide_controller_init_custom(which_bus, intf, NULL); */ |
| 332 | | #endif |
| 333 | | } |
| 334 | | |
| 335 | | |
| 336 | | static DEVICE_IMAGE_LOAD(mess_ide) |
| 337 | | { |
| 338 | | /* old code from idedrive.c */ |
| 339 | | #ifdef UNUSED_FUNCTION |
| 340 | | int result, which_bus, which_address; |
| 341 | | struct ide_interface *intf; |
| 342 | | device_image_load_func parent_load; |
| 343 | | |
| 344 | | /* get the basics */ |
| 345 | | ide_get_params(image, &which_bus, &which_address, &intf, NULL, &parent_load, NULL); |
| 346 | | |
| 347 | | /* call the parent load function */ |
| 348 | | result = parent_load(image); |
| 349 | | if (result != IMAGE_INIT_PASS) |
| 350 | | return result; |
| 351 | | |
| 352 | | /* configure IDE */ |
| 353 | | /* FIXME IDE */ |
| 354 | | /* ide_controller_init_custom(which_bus, intf, mess_hd_get_chd_file(image)); */ |
| 355 | | /* ide_controller_reset(which_bus); */ |
| 356 | | return IMAGE_INIT_PASS; |
| 357 | | #endif |
| 358 | | return device_load_mess_hd( image ); |
| 359 | | } |
| 360 | | |
| 361 | | |
| 362 | | static DEVICE_IMAGE_UNLOAD(mess_ide) |
| 363 | | { |
| 364 | | /* old code from idedrive.c */ |
| 365 | | #ifdef UNUSED_FUNCTION |
| 366 | | int which_bus, which_address; |
| 367 | | struct ide_interface *intf; |
| 368 | | device_image_unload_func parent_unload; |
| 369 | | |
| 370 | | /* get the basics */ |
| 371 | | ide_get_params(image, &which_bus, &which_address, &intf, NULL, NULL, &parent_unload); |
| 372 | | |
| 373 | | /* call the parent unload function */ |
| 374 | | parent_unload(image); |
| 375 | | |
| 376 | | /* configure IDE */ |
| 377 | | /* FIXME IDE */ |
| 378 | | /* ide_controller_init_custom(which_bus, intf, NULL); */ |
| 379 | | /* ide_controller_reset(which_bus); */ |
| 380 | | #endif |
| 381 | | device_unload_mess_hd( image ); |
| 382 | | } |
| 383 | | |
| 384 | | |
| 385 | | DEVICE_GET_INFO(mess_ide) |
| 386 | | { |
| 387 | | switch( state ) |
| 388 | | { |
| 389 | | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 390 | | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(mess_ide); break; |
| 391 | | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(mess_ide); break; |
| 392 | | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(mess_ide); break; |
| 393 | | |
| 394 | | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 395 | | case DEVINFO_STR_NAME: strcpy(info->s, "IDE harddisk"); break; |
| 396 | | case DEVINFO_STR_IMAGE_INSTANCE_NAME: strcpy(info->s, "ideharddrive"); break; |
| 397 | | case DEVINFO_STR_IMAGE_BRIEF_INSTANCE_NAME: strcpy(info->s, "idehd"); break; |
| 398 | | |
| 399 | | default: DEVICE_GET_INFO_CALL( mess_hd ); break; |
| 400 | | } |
| 401 | | } |
| 402 | | |
| 403 | | DEFINE_LEGACY_IMAGE_DEVICE(HARDDISK, mess_hd); |
| 404 | | DEFINE_LEGACY_IMAGE_DEVICE(IDE_HARDDISK, mess_ide); |
src/mess/devices/cassette.c
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | cassette.c |
| 4 | | |
| 5 | | MESS interface to the cassette image abstraction code |
| 6 | | |
| 7 | | *********************************************************************/ |
| 8 | | |
| 9 | | #include "emu.h" |
| 10 | | #include "imageutl.h" |
| 11 | | #include "cassette.h" |
| 12 | | #include "formats/cassimg.h" |
| 13 | | #include "ui.h" |
| 14 | | |
| 15 | | |
| 16 | | #define CASSETTE_TAG "cassette" |
| 17 | | #define ANIMATION_FPS 1 |
| 18 | | #define ANIMATION_FRAMES 4 |
| 19 | | |
| 20 | | #define VERBOSE 0 |
| 21 | | #define LOG(x) do { if (VERBOSE) logerror x; } while (0) |
| 22 | | |
| 23 | | |
| 24 | | typedef struct _dev_cassette_t dev_cassette_t; |
| 25 | | struct _dev_cassette_t |
| 26 | | { |
| 27 | | const cassette_config *config; |
| 28 | | cassette_image *cassette; |
| 29 | | cassette_state state; |
| 30 | | double position; |
| 31 | | double position_time; |
| 32 | | INT32 value; |
| 33 | | }; |
| 34 | | |
| 35 | | |
| 36 | | /* Default cassette_config for drivers only wav files */ |
| 37 | | const cassette_config default_cassette_config = |
| 38 | | { |
| 39 | | cassette_default_formats, |
| 40 | | NULL, |
| 41 | | CASSETTE_PLAY, |
| 42 | | NULL |
| 43 | | }; |
| 44 | | |
| 45 | | |
| 46 | | INLINE dev_cassette_t *get_safe_token(device_t *device) |
| 47 | | { |
| 48 | | assert( device != NULL ); |
| 49 | | assert( device->type() == CASSETTE ); |
| 50 | | return (dev_cassette_t *) downcast<legacy_device_base *>(device)->token(); |
| 51 | | } |
| 52 | | |
| 53 | | |
| 54 | | /********************************************************************* |
| 55 | | cassette IO |
| 56 | | *********************************************************************/ |
| 57 | | |
| 58 | | INLINE int cassette_is_motor_on(device_t *device) |
| 59 | | { |
| 60 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 61 | | |
| 62 | | if ((cassette->state & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED) |
| 63 | | return FALSE; |
| 64 | | if ((cassette->state & CASSETTE_MASK_MOTOR) != CASSETTE_MOTOR_ENABLED) |
| 65 | | return FALSE; |
| 66 | | return TRUE; |
| 67 | | } |
| 68 | | |
| 69 | | |
| 70 | | |
| 71 | | static void cassette_update(device_t *device) |
| 72 | | { |
| 73 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 74 | | double cur_time = attotime_to_double(timer_get_time(device->machine)); |
| 75 | | |
| 76 | | if (cassette_is_motor_on(device)) |
| 77 | | { |
| 78 | | double new_position = cassette->position + (cur_time - cassette->position_time); |
| 79 | | |
| 80 | | switch(cassette->state & CASSETTE_MASK_UISTATE) { |
| 81 | | case CASSETTE_RECORD: |
| 82 | | cassette_put_sample(cassette->cassette, 0, cassette->position, new_position - cassette->position, cassette->value); |
| 83 | | break; |
| 84 | | |
| 85 | | case CASSETTE_PLAY: |
| 86 | | if ( cassette->cassette ) |
| 87 | | { |
| 88 | | cassette_get_sample(cassette->cassette, 0, new_position, 0.0, &cassette->value); |
| 89 | | /* See if reached end of tape */ |
| 90 | | double length = cassette_get_length(device); |
| 91 | | if (new_position > length) |
| 92 | | { |
| 93 | | cassette->state = (cassette_state)(( cassette->state & ~CASSETTE_MASK_UISTATE ) | CASSETTE_STOPPED); |
| 94 | | new_position = length; |
| 95 | | } |
| 96 | | } |
| 97 | | break; |
| 98 | | } |
| 99 | | cassette->position = new_position; |
| 100 | | } |
| 101 | | cassette->position_time = cur_time; |
| 102 | | } |
| 103 | | |
| 104 | | |
| 105 | | |
| 106 | | cassette_state cassette_get_state(device_t *device) |
| 107 | | { |
| 108 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 109 | | return cassette->state; |
| 110 | | } |
| 111 | | |
| 112 | | |
| 113 | | |
| 114 | | void cassette_change_state(device_t *device, cassette_state state, cassette_state mask) |
| 115 | | { |
| 116 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 117 | | cassette_state new_state; |
| 118 | | |
| 119 | | new_state = cassette->state; |
| 120 | | new_state = (cassette_state)(new_state & ~mask); |
| 121 | | new_state = (cassette_state)(new_state | (state & mask)); |
| 122 | | |
| 123 | | if (new_state != cassette->state) |
| 124 | | { |
| 125 | | cassette_update(device); |
| 126 | | cassette->state = new_state; |
| 127 | | } |
| 128 | | } |
| 129 | | |
| 130 | | |
| 131 | | |
| 132 | | void cassette_set_state(device_t *device, cassette_state state) |
| 133 | | { |
| 134 | | cassette_change_state(device, state, (cassette_state)(~0)); |
| 135 | | } |
| 136 | | |
| 137 | | |
| 138 | | |
| 139 | | double cassette_input(device_t *device) |
| 140 | | { |
| 141 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 142 | | INT32 sample; |
| 143 | | double double_value; |
| 144 | | |
| 145 | | cassette_update(device); |
| 146 | | sample = cassette->value; |
| 147 | | double_value = sample / ((double) 0x7FFFFFFF); |
| 148 | | |
| 149 | | LOG(("cassette_input(): time_index=%g value=%g\n", cassette->position, double_value)); |
| 150 | | |
| 151 | | return double_value; |
| 152 | | } |
| 153 | | |
| 154 | | |
| 155 | | |
| 156 | | void cassette_output(device_t *device, double value) |
| 157 | | { |
| 158 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 159 | | |
| 160 | | if (((cassette->state & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD) && (cassette->value != value)) |
| 161 | | { |
| 162 | | cassette_update(device); |
| 163 | | |
| 164 | | value = MIN(value, 1.0); |
| 165 | | value = MAX(value, -1.0); |
| 166 | | |
| 167 | | cassette->value = (INT32) (value * 0x7FFFFFFF); |
| 168 | | } |
| 169 | | } |
| 170 | | |
| 171 | | |
| 172 | | |
| 173 | | cassette_image *cassette_get_image(device_t *device) |
| 174 | | { |
| 175 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 176 | | |
| 177 | | return cassette->cassette; |
| 178 | | } |
| 179 | | |
| 180 | | |
| 181 | | |
| 182 | | double cassette_get_position(device_t *device) |
| 183 | | { |
| 184 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 185 | | double position; |
| 186 | | |
| 187 | | position = cassette->position; |
| 188 | | |
| 189 | | if (cassette_is_motor_on(device)) |
| 190 | | position += attotime_to_double(timer_get_time(device->machine)) - cassette->position_time; |
| 191 | | return position; |
| 192 | | } |
| 193 | | |
| 194 | | |
| 195 | | |
| 196 | | double cassette_get_length(device_t *device) |
| 197 | | { |
| 198 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 199 | | struct CassetteInfo info; |
| 200 | | |
| 201 | | cassette_get_info(cassette->cassette, &info); |
| 202 | | return ((double) info.sample_count) / info.sample_frequency; |
| 203 | | } |
| 204 | | |
| 205 | | |
| 206 | | |
| 207 | | void cassette_seek(device_t *device, double time, int origin) |
| 208 | | { |
| 209 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 210 | | |
| 211 | | double length; |
| 212 | | |
| 213 | | cassette_update(device); |
| 214 | | |
| 215 | | length = cassette_get_length(device); |
| 216 | | |
| 217 | | switch(origin) { |
| 218 | | case SEEK_SET: |
| 219 | | break; |
| 220 | | |
| 221 | | case SEEK_END: |
| 222 | | time += length; |
| 223 | | break; |
| 224 | | |
| 225 | | case SEEK_CUR: |
| 226 | | time += cassette_get_position(device); |
| 227 | | break; |
| 228 | | } |
| 229 | | |
| 230 | | /* clip position into legal bounds */ |
| 231 | | if (time < 0) |
| 232 | | time = 0; |
| 233 | | else |
| 234 | | if (time > length) |
| 235 | | time = length; |
| 236 | | |
| 237 | | cassette->position = time; |
| 238 | | } |
| 239 | | |
| 240 | | |
| 241 | | |
| 242 | | /********************************************************************* |
| 243 | | cassette device init/load/unload/specify |
| 244 | | *********************************************************************/ |
| 245 | | |
| 246 | | static DEVICE_START( cassette ) |
| 247 | | { |
| 248 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 249 | | |
| 250 | | /* set to default state */ |
| 251 | | cassette->config = (const cassette_config*)device->baseconfig().static_config(); |
| 252 | | cassette->cassette = NULL; |
| 253 | | cassette->state = cassette->config->default_state; |
| 254 | | } |
| 255 | | |
| 256 | | |
| 257 | | |
| 258 | | static DEVICE_IMAGE_LOAD( cassette ) |
| 259 | | { |
| 260 | | device_t *device = &image.device(); |
| 261 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 262 | | casserr_t err; |
| 263 | | int cassette_flags; |
| 264 | | const struct CassetteFormat * const *formats; |
| 265 | | const struct CassetteOptions *create_opts; |
| 266 | | const char *extension; |
| 267 | | int is_writable; |
| 268 | | |
| 269 | | /* figure out the cassette format */ |
| 270 | | formats = cassette->config->formats; |
| 271 | | |
| 272 | | if (image.has_been_created()) |
| 273 | | { |
| 274 | | /* creating an image */ |
| 275 | | create_opts = cassette->config->create_opts; |
| 276 | | err = cassette_create(device->machine, (void *) &image, &image_ioprocs, &wavfile_format, create_opts, CASSETTE_FLAG_READWRITE|CASSETTE_FLAG_SAVEONEXIT, &cassette->cassette); |
| 277 | | if (err) |
| 278 | | goto error; |
| 279 | | } |
| 280 | | else |
| 281 | | { |
| 282 | | /* opening an image */ |
| 283 | | do |
| 284 | | { |
| 285 | | is_writable = image.is_writable(); |
| 286 | | cassette_flags = is_writable ? (CASSETTE_FLAG_READWRITE|CASSETTE_FLAG_SAVEONEXIT) : CASSETTE_FLAG_READONLY; |
| 287 | | extension = image.filetype(); |
| 288 | | err = cassette_open_choices(device->machine,(void *) &image, &image_ioprocs, extension, formats, cassette_flags, &cassette->cassette); |
| 289 | | |
| 290 | | /* this is kind of a hack */ |
| 291 | | if (err && is_writable) |
| 292 | | image.make_readonly(); |
| 293 | | } |
| 294 | | while(err && is_writable); |
| 295 | | |
| 296 | | if (err) |
| 297 | | goto error; |
| 298 | | } |
| 299 | | |
| 300 | | /* set to default state, but only change the UI state */ |
| 301 | | cassette_change_state(device, cassette->config->default_state, CASSETTE_MASK_UISTATE); |
| 302 | | |
| 303 | | /* reset the position */ |
| 304 | | cassette->position = 0.0; |
| 305 | | cassette->position_time = attotime_to_double(timer_get_time(device->machine)); |
| 306 | | |
| 307 | | return IMAGE_INIT_PASS; |
| 308 | | |
| 309 | | error: |
| 310 | | return IMAGE_INIT_FAIL; |
| 311 | | } |
| 312 | | |
| 313 | | |
| 314 | | |
| 315 | | static DEVICE_IMAGE_UNLOAD( cassette ) |
| 316 | | { |
| 317 | | device_t *device = &image.device(); |
| 318 | | dev_cassette_t *cassette = get_safe_token( device ); |
| 319 | | |
| 320 | | /* if we are recording, write the value to the image */ |
| 321 | | if ((cassette->state & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD) |
| 322 | | cassette_update(device); |
| 323 | | |
| 324 | | /* close out the cassette */ |
| 325 | | cassette_close(cassette->cassette); |
| 326 | | cassette->cassette = NULL; |
| 327 | | |
| 328 | | /* set to default state, but only change the UI state */ |
| 329 | | cassette_change_state(device, CASSETTE_STOPPED, CASSETTE_MASK_UISTATE); |
| 330 | | } |
| 331 | | |
| 332 | | |
| 333 | | |
| 334 | | /* |
| 335 | | display a small tape icon, with the current position in the tape image |
| 336 | | */ |
| 337 | | static DEVICE_IMAGE_DISPLAY(cassette) |
| 338 | | { |
| 339 | | device_t *device = &image.device(); |
| 340 | | char buf[65]; |
| 341 | | float x, y; |
| 342 | | int n; |
| 343 | | double position, length; |
| 344 | | cassette_state uistate; |
| 345 | | device_t *dev; |
| 346 | | static const UINT8 shapes[8] = { 0x2d, 0x5c, 0x7c, 0x2f, 0x2d, 0x20, 0x20, 0x20 }; |
| 347 | | |
| 348 | | /* abort if we should not be showing the image */ |
| 349 | | if (!image.exists()) |
| 350 | | return; |
| 351 | | if (!cassette_is_motor_on(device)) |
| 352 | | return; |
| 353 | | |
| 354 | | /* figure out where we are in the cassette */ |
| 355 | | position = cassette_get_position(device); |
| 356 | | length = cassette_get_length(device); |
| 357 | | uistate = (cassette_state)(cassette_get_state(device) & CASSETTE_MASK_UISTATE); |
| 358 | | |
| 359 | | /* choose a location on the screen */ |
| 360 | | x = 0.2f; |
| 361 | | y = 0.5f; |
| 362 | | |
| 363 | | dev = device->machine->m_devicelist.first(CASSETTE ); |
| 364 | | |
| 365 | | while ( dev && strcmp( dev->tag(), device->tag() ) ) |
| 366 | | { |
| 367 | | y += 1; |
| 368 | | dev = dev->typenext(); |
| 369 | | } |
| 370 | | |
| 371 | | y *= ui_get_line_height(*device->machine) + 2.0f * UI_BOX_TB_BORDER; |
| 372 | | /* choose which frame of the animation we are at */ |
| 373 | | n = ((int) position / ANIMATION_FPS) % ANIMATION_FRAMES; |
| 374 | | /* Since you can have anything in a BDF file, we will use crude ascii characters instead */ |
| 375 | | snprintf(buf, ARRAY_LENGTH(buf), "%c%c %c %02d:%02d (%04d) [%02d:%02d (%04d)]", |
| 376 | | #if 0 |
| 377 | | /* THE ANIMATION HASN'T WORKED SINCE 0.114 - LEFT HERE FOR REFERENCE */ |
| 378 | | /* NEVER SEEN THE PLAY / RECORD ICONS */ |
| 379 | | /* character pairs 2-3, 4-5, 6-7, 8-9 form little tape cassette images */ |
| 380 | | n * 2 + 2, /* cassette icon left */ |
| 381 | | n * 2 + 3, /* cassette icon right */ |
| 382 | | (uistate == CASSETTE_PLAY) ? 16 : 14, /* play or record icon */ |
| 383 | | #else |
| 384 | | shapes[n], /* cassette icon left */ |
| 385 | | shapes[n|4], /* cassette icon right */ |
| 386 | | (uistate == CASSETTE_PLAY) ? 0x50 : 0x52, /* play (P) or record (R) */ |
| 387 | | #endif |
| 388 | | ((int) position / 60), |
| 389 | | ((int) position % 60), |
| 390 | | (int) position, |
| 391 | | ((int) length / 60), |
| 392 | | ((int) length % 60), |
| 393 | | (int) length); |
| 394 | | |
| 395 | | /* draw the cassette */ |
| 396 | | ui_draw_text_box(&device->machine->render().ui_container(), buf, JUSTIFY_LEFT, x, y, UI_BACKGROUND_COLOR); |
| 397 | | } |
| 398 | | |
| 399 | | /*------------------------------------------------- |
| 400 | | DEVICE_IMAGE_SOFTLIST_LOAD(cassette) |
| 401 | | -------------------------------------------------*/ |
| 402 | | static DEVICE_IMAGE_SOFTLIST_LOAD(cassette) |
| 403 | | { |
| 404 | | return image.load_software(swlist, swname, start_entry); |
| 405 | | } |
| 406 | | |
| 407 | | DEVICE_GET_INFO(cassette) |
| 408 | | { |
| 409 | | switch( state ) |
| 410 | | { |
| 411 | | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 412 | | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(dev_cassette_t); break; |
| 413 | | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; |
| 414 | | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_CASSETTE; break; |
| 415 | | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 416 | | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break; |
| 417 | | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break; |
| 418 | | |
| 419 | | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 420 | | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(cassette); break; |
| 421 | | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(cassette); break; |
| 422 | | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(cassette); break; |
| 423 | | case DEVINFO_FCT_IMAGE_DISPLAY: info->f = (genf *) DEVICE_IMAGE_DISPLAY_NAME(cassette); break; |
| 424 | | case DEVINFO_FCT_IMAGE_SOFTLIST_LOAD: info->f = (genf *) DEVICE_IMAGE_SOFTLIST_LOAD_NAME(cassette); break; |
| 425 | | |
| 426 | | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 427 | | case DEVINFO_STR_NAME: strcpy(info->s, "Cassette"); break; |
| 428 | | case DEVINFO_STR_FAMILY: strcpy(info->s, "Cassette"); break; |
| 429 | | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 430 | | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: |
| 431 | | if ( device && device->static_config() ) |
| 432 | | { |
| 433 | | const struct CassetteFormat * const *formats = ((cassette_config *)device->static_config())->formats; |
| 434 | | int i; |
| 435 | | |
| 436 | | /* set up a temporary string */ |
| 437 | | info->s[0] = '\0'; |
| 438 | | |
| 439 | | for ( i = 0; formats[i]; i++ ) |
| 440 | | image_specify_extension( info->s, 256, formats[i]->extensions ); |
| 441 | | } |
| 442 | | break; |
| 443 | | case DEVINFO_STR_IMAGE_INTERFACE: |
| 444 | | if ( device && device->static_config() && ((cassette_config *)device->static_config())->interface) |
| 445 | | { |
| 446 | | strcpy(info->s, ((cassette_config *)device->static_config())->interface ); |
| 447 | | } |
| 448 | | break; |
| 449 | | } |
| 450 | | } |
| 451 | | |
| 452 | | DEFINE_LEGACY_IMAGE_DEVICE(CASSETTE, cassette); |
src/mess/devices/flopdrv.c
| r9927 | r9928 | |
| 1 | | /* |
| 2 | | This code handles the floppy drives. |
| 3 | | All FDD actions should be performed using these functions. |
| 4 | | |
| 5 | | The functions are emulated and a disk image is used. |
| 6 | | |
| 7 | | Real disk operation: |
| 8 | | - set unit id |
| 9 | | |
| 10 | | TODO: |
| 11 | | - Override write protect if disk image has been opened in read mode |
| 12 | | */ |
| 13 | | |
| 14 | | #include "emu.h" |
| 15 | | #include "imageutl.h" |
| 16 | | #include "flopdrv.h" |
| 17 | | |
| 18 | | #define VERBOSE 0 |
| 19 | | #define LOG(x) do { if (VERBOSE) logerror x; } while (0) |
| 20 | | |
| 21 | | /*************************************************************************** |
| 22 | | CONSTANTS |
| 23 | | ***************************************************************************/ |
| 24 | | |
| 25 | | #define FLOPDRVTAG "flopdrv" |
| 26 | | #define LOG_FLOPPY 0 |
| 27 | | |
| 28 | | /*************************************************************************** |
| 29 | | TYPE DEFINITIONS |
| 30 | | ***************************************************************************/ |
| 31 | | |
| 32 | | typedef struct _floppy_drive floppy_drive; |
| 33 | | struct _floppy_drive |
| 34 | | { |
| 35 | | /* callbacks */ |
| 36 | | devcb_resolved_write_line out_idx_func; |
| 37 | | devcb_resolved_read_line in_mon_func; |
| 38 | | devcb_resolved_write_line out_tk00_func; |
| 39 | | devcb_resolved_write_line out_wpt_func; |
| 40 | | devcb_resolved_write_line out_rdy_func; |
| 41 | | devcb_resolved_write_line out_dskchg_func; |
| 42 | | |
| 43 | | /* state of input lines */ |
| 44 | | int drtn; /* direction */ |
| 45 | | int stp; /* step */ |
| 46 | | int wtg; /* write gate */ |
| 47 | | int mon; /* motor on */ |
| 48 | | |
| 49 | | /* state of output lines */ |
| 50 | | int idx; /* index pulse */ |
| 51 | | int tk00; /* track 00 */ |
| 52 | | int wpt; /* write protect */ |
| 53 | | int rdy; /* ready */ |
| 54 | | int dskchg; /* disk changed */ |
| 55 | | |
| 56 | | /* drive select logic */ |
| 57 | | int drive_id; |
| 58 | | int active; |
| 59 | | |
| 60 | | const floppy_config *config; |
| 61 | | |
| 62 | | /* flags */ |
| 63 | | int flags; |
| 64 | | /* maximum track allowed */ |
| 65 | | int max_track; |
| 66 | | /* num sides */ |
| 67 | | int num_sides; |
| 68 | | /* current track - this may or may not relate to the present cylinder number |
| 69 | | stored by the fdc */ |
| 70 | | int current_track; |
| 71 | | |
| 72 | | /* index pulse timer */ |
| 73 | | void *index_timer; |
| 74 | | /* index pulse callback */ |
| 75 | | void (*index_pulse_callback)(device_t *controller,device_t *image, int state); |
| 76 | | /* rotation per minute => gives index pulse frequency */ |
| 77 | | float rpm; |
| 78 | | |
| 79 | | void (*ready_state_change_callback)(device_t *controller,device_t *img, int state); |
| 80 | | |
| 81 | | int id_index; |
| 82 | | |
| 83 | | device_t *controller; |
| 84 | | |
| 85 | | floppy_image *floppy; |
| 86 | | int track; |
| 87 | | void (*load_proc)(device_image_interface &image); |
| 88 | | void (*unload_proc)(device_image_interface &image); |
| 89 | | void *custom_data; |
| 90 | | int floppy_drive_type; |
| 91 | | }; |
| 92 | | |
| 93 | | |
| 94 | | typedef struct _floppy_error_map floppy_error_map; |
| 95 | | struct _floppy_error_map |
| 96 | | { |
| 97 | | floperr_t ferr; |
| 98 | | image_error_t ierr; |
| 99 | | const char *message; |
| 100 | | }; |
| 101 | | |
| 102 | | |
| 103 | | |
| 104 | | /*************************************************************************** |
| 105 | | GLOBAL VARIABLES |
| 106 | | ***************************************************************************/ |
| 107 | | |
| 108 | | static const floppy_error_map errmap[] = |
| 109 | | { |
| 110 | | { FLOPPY_ERROR_SUCCESS, IMAGE_ERROR_SUCCESS }, |
| 111 | | { FLOPPY_ERROR_INTERNAL, IMAGE_ERROR_INTERNAL }, |
| 112 | | { FLOPPY_ERROR_UNSUPPORTED, IMAGE_ERROR_UNSUPPORTED }, |
| 113 | | { FLOPPY_ERROR_OUTOFMEMORY, IMAGE_ERROR_OUTOFMEMORY }, |
| 114 | | { FLOPPY_ERROR_INVALIDIMAGE, IMAGE_ERROR_INVALIDIMAGE } |
| 115 | | }; |
| 116 | | |
| 117 | | /*************************************************************************** |
| 118 | | IMPLEMENTATION |
| 119 | | ***************************************************************************/ |
| 120 | | DECLARE_LEGACY_IMAGE_DEVICE(FLOPPY_APPLE, apple525); |
| 121 | | DECLARE_LEGACY_IMAGE_DEVICE(FLOPPY_SONY, sonydriv); |
| 122 | | |
| 123 | | INLINE floppy_drive *get_safe_token(device_t *device) |
| 124 | | { |
| 125 | | assert( device != NULL ); |
| 126 | | assert( device->type() == FLOPPY || device->type() == FLOPPY_APPLE || device->type() == FLOPPY_SONY); |
| 127 | | return (floppy_drive *) downcast<legacy_device_base *>(device)->token(); |
| 128 | | } |
| 129 | | |
| 130 | | floppy_image *flopimg_get_image(device_t *image) |
| 131 | | { |
| 132 | | return get_safe_token(image)->floppy; |
| 133 | | } |
| 134 | | |
| 135 | | void *flopimg_get_custom_data(device_t *image) |
| 136 | | { |
| 137 | | floppy_drive *flopimg = get_safe_token( image ); |
| 138 | | return flopimg->custom_data; |
| 139 | | } |
| 140 | | |
| 141 | | void flopimg_alloc_custom_data(device_t *image,void *custom) |
| 142 | | { |
| 143 | | floppy_drive *flopimg = get_safe_token( image ); |
| 144 | | flopimg->custom_data = custom; |
| 145 | | } |
| 146 | | |
| 147 | | static int flopimg_get_sectors_per_track(device_t *image, int side) |
| 148 | | { |
| 149 | | floperr_t err; |
| 150 | | int sector_count; |
| 151 | | floppy_drive *flopimg = get_safe_token( image ); |
| 152 | | |
| 153 | | if (!flopimg || !flopimg->floppy) |
| 154 | | return 0; |
| 155 | | |
| 156 | | err = floppy_get_sector_count(flopimg->floppy, side, flopimg->track, §or_count); |
| 157 | | if (err) |
| 158 | | return 0; |
| 159 | | return sector_count; |
| 160 | | } |
| 161 | | |
| 162 | | static void flopimg_get_id_callback(device_t *image, chrn_id *id, int id_index, int side) |
| 163 | | { |
| 164 | | floppy_drive *flopimg; |
| 165 | | int cylinder, sector, N; |
| 166 | | unsigned long flags; |
| 167 | | UINT32 sector_length; |
| 168 | | |
| 169 | | flopimg = get_safe_token( image ); |
| 170 | | if (!flopimg || !flopimg->floppy) |
| 171 | | return; |
| 172 | | |
| 173 | | floppy_get_indexed_sector_info(flopimg->floppy, side, flopimg->track, id_index, &cylinder, &side, §or, §or_length, &flags); |
| 174 | | |
| 175 | | N = compute_log2(sector_length); |
| 176 | | |
| 177 | | id->C = cylinder; |
| 178 | | id->H = side; |
| 179 | | id->R = sector; |
| 180 | | id->data_id = id_index; |
| 181 | | id->flags = flags; |
| 182 | | id->N = ((N >= 7) && (N <= 10)) ? N - 7 : 0; |
| 183 | | } |
| 184 | | |
| 185 | | static void log_readwrite(const char *name, int head, int track, int sector, const char *buf, int length) |
| 186 | | { |
| 187 | | char membuf[1024]; |
| 188 | | int i; |
| 189 | | for (i = 0; i < length; i++) |
| 190 | | sprintf(membuf + i*2, "%02x", (int) (UINT8) buf[i]); |
| 191 | | logerror("%s: head=%i track=%i sector=%i buffer='%s'\n", name, head, track, sector, membuf); |
| 192 | | } |
| 193 | | |
| 194 | | static void floppy_drive_set_geometry_absolute(device_t *img, int tracks, int sides) |
| 195 | | { |
| 196 | | floppy_drive *pDrive = get_safe_token( img ); |
| 197 | | pDrive->max_track = tracks; |
| 198 | | pDrive->num_sides = sides; |
| 199 | | } |
| 200 | | |
| 201 | | void floppy_drive_set_geometry(device_t *img, floppy_type_t type) |
| 202 | | { |
| 203 | | floppy_drive_set_geometry_absolute(img, type.max_track_number, type.head_number); |
| 204 | | } |
| 205 | | |
| 206 | | static TIMER_CALLBACK(floppy_drive_index_callback); |
| 207 | | |
| 208 | | /* this is called on device init */ |
| 209 | | static void floppy_drive_init(device_t *img) |
| 210 | | { |
| 211 | | floppy_drive *pDrive = get_safe_token( img ); |
| 212 | | |
| 213 | | /* initialise flags */ |
| 214 | | pDrive->flags = 0; |
| 215 | | pDrive->index_pulse_callback = NULL; |
| 216 | | pDrive->ready_state_change_callback = NULL; |
| 217 | | pDrive->index_timer = timer_alloc(img->machine, floppy_drive_index_callback, (void *) img); |
| 218 | | pDrive->idx = 0; |
| 219 | | |
| 220 | | floppy_drive_set_geometry(img, ((floppy_config*)img->baseconfig().static_config())->floppy_type); |
| 221 | | |
| 222 | | /* initialise id index - not so important */ |
| 223 | | pDrive->id_index = 0; |
| 224 | | /* initialise track */ |
| 225 | | pDrive->current_track = 0; |
| 226 | | |
| 227 | | /* default RPM */ |
| 228 | | pDrive->rpm = 300; |
| 229 | | |
| 230 | | pDrive->controller = NULL; |
| 231 | | |
| 232 | | pDrive->custom_data = NULL; |
| 233 | | |
| 234 | | pDrive->floppy_drive_type = FLOPPY_TYPE_REGULAR; |
| 235 | | } |
| 236 | | |
| 237 | | /* index pulses at rpm/60 Hz, and stays high 1/20th of time */ |
| 238 | | static void floppy_drive_index_func(device_t *img) |
| 239 | | { |
| 240 | | floppy_drive *drive = get_safe_token( img ); |
| 241 | | |
| 242 | | double ms = 1000.0 / (drive->rpm / 60.0); |
| 243 | | |
| 244 | | if (drive->idx) |
| 245 | | { |
| 246 | | drive->idx = 0; |
| 247 | | timer_adjust_oneshot((emu_timer*)drive->index_timer, double_to_attotime(ms*19/20/1000.0), 0); |
| 248 | | } |
| 249 | | else |
| 250 | | { |
| 251 | | drive->idx = 1; |
| 252 | | timer_adjust_oneshot((emu_timer*)drive->index_timer, double_to_attotime(ms/20/1000.0), 0); |
| 253 | | } |
| 254 | | |
| 255 | | devcb_call_write_line(&drive->out_idx_func, drive->idx); |
| 256 | | |
| 257 | | if (drive->index_pulse_callback) |
| 258 | | drive->index_pulse_callback(drive->controller, img, drive->idx); |
| 259 | | } |
| 260 | | |
| 261 | | static TIMER_CALLBACK(floppy_drive_index_callback) |
| 262 | | { |
| 263 | | device_t *image = (device_t *) ptr; |
| 264 | | floppy_drive_index_func(image); |
| 265 | | } |
| 266 | | |
| 267 | | /*************************************************************************/ |
| 268 | | /* IO_FLOPPY device functions */ |
| 269 | | |
| 270 | | /* set flag state */ |
| 271 | | void floppy_drive_set_flag_state(device_t *img, int flag, int state) |
| 272 | | { |
| 273 | | floppy_drive *drv = get_safe_token( img ); |
| 274 | | int prev_state; |
| 275 | | int new_state; |
| 276 | | |
| 277 | | /* get old state */ |
| 278 | | prev_state = drv->flags & flag; |
| 279 | | |
| 280 | | /* set new state */ |
| 281 | | drv->flags &= ~flag; |
| 282 | | if (state) |
| 283 | | drv->flags |= flag; |
| 284 | | |
| 285 | | /* get new state */ |
| 286 | | new_state = drv->flags & flag; |
| 287 | | |
| 288 | | /* changed state? */ |
| 289 | | if (prev_state ^ new_state) |
| 290 | | { |
| 291 | | if (flag & FLOPPY_DRIVE_READY) |
| 292 | | { |
| 293 | | /* trigger state change callback */ |
| 294 | | devcb_call_write_line(&drv->out_rdy_func, new_state ? ASSERT_LINE : CLEAR_LINE); |
| 295 | | |
| 296 | | if (drv->ready_state_change_callback) |
| 297 | | drv->ready_state_change_callback(drv->controller, img, new_state); |
| 298 | | } |
| 299 | | } |
| 300 | | } |
| 301 | | |
| 302 | | /* for pc, drive is always ready, for amstrad,pcw,spectrum it is only ready under |
| 303 | | a fixed set of circumstances */ |
| 304 | | /* use this to set ready state of drive */ |
| 305 | | void floppy_drive_set_ready_state(device_t *img, int state, int flag) |
| 306 | | { |
| 307 | | floppy_drive *drive = get_safe_token(img); |
| 308 | | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 309 | | if (flag) |
| 310 | | { |
| 311 | | /* set ready only if drive is present, disk is in the drive, |
| 312 | | and disk motor is on - for Amstrad, Spectrum and PCW*/ |
| 313 | | |
| 314 | | /* drive present? */ |
| 315 | | /* disk inserted? */ |
| 316 | | if (image->exists()) |
| 317 | | { |
| 318 | | if (drive->mon == CLEAR_LINE) |
| 319 | | { |
| 320 | | /* set state */ |
| 321 | | floppy_drive_set_flag_state(img, FLOPPY_DRIVE_READY, state); |
| 322 | | return; |
| 323 | | } |
| 324 | | } |
| 325 | | floppy_drive_set_flag_state(img, FLOPPY_DRIVE_READY, 0); |
| 326 | | } |
| 327 | | else |
| 328 | | { |
| 329 | | /* force ready state - for PC driver */ |
| 330 | | floppy_drive_set_flag_state(img, FLOPPY_DRIVE_READY, state); |
| 331 | | } |
| 332 | | } |
| 333 | | |
| 334 | | /* get flag state */ |
| 335 | | int floppy_drive_get_flag_state(device_t *img, int flag) |
| 336 | | { |
| 337 | | floppy_drive *drv = get_safe_token( img ); |
| 338 | | int drive_flags; |
| 339 | | int flags; |
| 340 | | |
| 341 | | flags = 0; |
| 342 | | |
| 343 | | drive_flags = drv->flags; |
| 344 | | |
| 345 | | /* these flags are independant of a real drive/disk image */ |
| 346 | | flags |= drive_flags & (FLOPPY_DRIVE_READY | FLOPPY_DRIVE_INDEX); |
| 347 | | |
| 348 | | flags &= flag; |
| 349 | | |
| 350 | | return flags; |
| 351 | | } |
| 352 | | |
| 353 | | |
| 354 | | void floppy_drive_seek(device_t *img, signed int signed_tracks) |
| 355 | | { |
| 356 | | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 357 | | floppy_drive *pDrive; |
| 358 | | |
| 359 | | pDrive = get_safe_token( img ); |
| 360 | | |
| 361 | | LOG(("seek from: %d delta: %d\n",pDrive->current_track, signed_tracks)); |
| 362 | | |
| 363 | | /* update position */ |
| 364 | | pDrive->current_track+=signed_tracks; |
| 365 | | |
| 366 | | if (pDrive->current_track<0) |
| 367 | | { |
| 368 | | pDrive->current_track = 0; |
| 369 | | } |
| 370 | | else |
| 371 | | if (pDrive->current_track>=pDrive->max_track) |
| 372 | | { |
| 373 | | pDrive->current_track = pDrive->max_track-1; |
| 374 | | } |
| 375 | | |
| 376 | | /* set track 0 flag */ |
| 377 | | pDrive->tk00 = (pDrive->current_track == 0) ? CLEAR_LINE : ASSERT_LINE; |
| 378 | | devcb_call_write_line(&pDrive->out_tk00_func, pDrive->tk00); |
| 379 | | |
| 380 | | /* clear disk changed flag */ |
| 381 | | pDrive->dskchg = ASSERT_LINE; |
| 382 | | //devcb_call_write_line(&flopimg->out_dskchg_func, flopimg->dskchg); |
| 383 | | |
| 384 | | /* inform disk image of step operation so it can cache information */ |
| 385 | | if (image->exists()) |
| 386 | | pDrive->track = pDrive->current_track; |
| 387 | | |
| 388 | | pDrive->id_index = 0; |
| 389 | | } |
| 390 | | |
| 391 | | |
| 392 | | /* this is not accurate. But it will do for now */ |
| 393 | | int floppy_drive_get_next_id(device_t *img, int side, chrn_id *id) |
| 394 | | { |
| 395 | | floppy_drive *pDrive; |
| 396 | | int spt; |
| 397 | | |
| 398 | | pDrive = get_safe_token( img ); |
| 399 | | |
| 400 | | /* get sectors per track */ |
| 401 | | spt = flopimg_get_sectors_per_track(img, side); |
| 402 | | |
| 403 | | /* set index */ |
| 404 | | if ((pDrive->id_index==(spt-1)) || (spt==0)) |
| 405 | | { |
| 406 | | floppy_drive_set_flag_state(img, FLOPPY_DRIVE_INDEX, 1); |
| 407 | | } |
| 408 | | else |
| 409 | | { |
| 410 | | floppy_drive_set_flag_state(img, FLOPPY_DRIVE_INDEX, 0); |
| 411 | | } |
| 412 | | |
| 413 | | /* get id */ |
| 414 | | if (spt!=0) |
| 415 | | { |
| 416 | | flopimg_get_id_callback(img, id, pDrive->id_index, side); |
| 417 | | } |
| 418 | | |
| 419 | | pDrive->id_index++; |
| 420 | | if (spt!=0) |
| 421 | | pDrive->id_index %= spt; |
| 422 | | else |
| 423 | | pDrive->id_index = 0; |
| 424 | | |
| 425 | | return (spt == 0) ? 0 : 1; |
| 426 | | } |
| 427 | | |
| 428 | | void floppy_drive_read_track_data_info_buffer(device_t *img, int side, void *ptr, int *length ) |
| 429 | | { |
| 430 | | floppy_drive *flopimg; |
| 431 | | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 432 | | |
| 433 | | if (image->exists()) |
| 434 | | { |
| 435 | | flopimg = get_safe_token( img ); |
| 436 | | if (!flopimg || !flopimg->floppy) |
| 437 | | return; |
| 438 | | |
| 439 | | floppy_read_track_data(flopimg->floppy, side, flopimg->track, ptr, *length); |
| 440 | | } |
| 441 | | } |
| 442 | | |
| 443 | | void floppy_drive_write_track_data_info_buffer(device_t *img, int side, const void *ptr, int *length ) |
| 444 | | { |
| 445 | | floppy_drive *flopimg; |
| 446 | | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 447 | | |
| 448 | | if (image->exists()) |
| 449 | | { |
| 450 | | flopimg = get_safe_token( img ); |
| 451 | | if (!flopimg || !flopimg->floppy) |
| 452 | | return; |
| 453 | | |
| 454 | | floppy_write_track_data(flopimg->floppy, side, flopimg->track, ptr, *length); |
| 455 | | } |
| 456 | | } |
| 457 | | |
| 458 | | void floppy_drive_format_sector(device_t *img, int side, int sector_index,int c,int h, int r, int n, int filler) |
| 459 | | { |
| 460 | | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 461 | | |
| 462 | | if (image->exists()) |
| 463 | | { |
| 464 | | /* if (drv->interface_.format_sector) |
| 465 | | drv->interface_.format_sector(img, side, sector_index,c, h, r, n, filler);*/ |
| 466 | | } |
| 467 | | } |
| 468 | | |
| 469 | | void floppy_drive_read_sector_data(device_t *img, int side, int index1, void *ptr, int length) |
| 470 | | { |
| 471 | | floppy_drive *flopimg; |
| 472 | | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 473 | | |
| 474 | | if (image->exists()) |
| 475 | | { |
| 476 | | flopimg = get_safe_token( img ); |
| 477 | | if (!flopimg || !flopimg->floppy) |
| 478 | | return; |
| 479 | | |
| 480 | | floppy_read_indexed_sector(flopimg->floppy, side, flopimg->track, index1, 0, ptr, length); |
| 481 | | |
| 482 | | if (LOG_FLOPPY) |
| 483 | | log_readwrite("sector_read", side, flopimg->track, index1, (const char *)ptr, length); |
| 484 | | |
| 485 | | } |
| 486 | | } |
| 487 | | |
| 488 | | void floppy_drive_write_sector_data(device_t *img, int side, int index1, const void *ptr,int length, int ddam) |
| 489 | | { |
| 490 | | floppy_drive *flopimg; |
| 491 | | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 492 | | |
| 493 | | if (image->exists()) |
| 494 | | { |
| 495 | | flopimg = get_safe_token( img ); |
| 496 | | if (!flopimg || !flopimg->floppy) |
| 497 | | return; |
| 498 | | |
| 499 | | if (LOG_FLOPPY) |
| 500 | | log_readwrite("sector_write", side, flopimg->track, index1, (const char *)ptr, length); |
| 501 | | |
| 502 | | floppy_write_indexed_sector(flopimg->floppy, side, flopimg->track, index1, 0, ptr, length, ddam); |
| 503 | | } |
| 504 | | } |
| 505 | | |
| 506 | | void floppy_install_load_proc(device_t *image, void (*proc)(device_image_interface &image)) |
| 507 | | { |
| 508 | | floppy_drive *flopimg = get_safe_token( image ); |
| 509 | | flopimg->load_proc = proc; |
| 510 | | } |
| 511 | | |
| 512 | | void floppy_install_unload_proc(device_t *image, void (*proc)(device_image_interface &image)) |
| 513 | | { |
| 514 | | floppy_drive *flopimg = get_safe_token( image ); |
| 515 | | flopimg->unload_proc = proc; |
| 516 | | } |
| 517 | | |
| 518 | | /* set the callback for the index pulse */ |
| 519 | | void floppy_drive_set_index_pulse_callback(device_t *img, void (*callback)(device_t *controller,device_t *image, int state)) |
| 520 | | { |
| 521 | | floppy_drive *pDrive = get_safe_token( img ); |
| 522 | | pDrive->index_pulse_callback = callback; |
| 523 | | } |
| 524 | | |
| 525 | | |
| 526 | | void floppy_drive_set_ready_state_change_callback(device_t *img, void (*callback)(device_t *controller,device_t *img, int state)) |
| 527 | | { |
| 528 | | floppy_drive *pDrive = get_safe_token( img ); |
| 529 | | pDrive->ready_state_change_callback = callback; |
| 530 | | } |
| 531 | | |
| 532 | | int floppy_drive_get_current_track(device_t *img) |
| 533 | | { |
| 534 | | floppy_drive *drv = get_safe_token( img ); |
| 535 | | return drv->current_track; |
| 536 | | } |
| 537 | | |
| 538 | | UINT64 floppy_drive_get_current_track_size(device_t *img, int head) |
| 539 | | { |
| 540 | | floppy_drive *drv = get_safe_token( img ); |
| 541 | | int size = 0; |
| 542 | | device_image_interface *image = dynamic_cast<device_image_interface *>(img); |
| 543 | | |
| 544 | | if (image->exists()) |
| 545 | | { |
| 546 | | size = floppy_get_track_size(drv->floppy, head, drv->current_track); |
| 547 | | } |
| 548 | | |
| 549 | | return size; |
| 550 | | } |
| 551 | | |
| 552 | | void floppy_drive_set_rpm(device_t *img, float rpm) |
| 553 | | { |
| 554 | | floppy_drive *drv = get_safe_token( img ); |
| 555 | | drv->rpm = rpm; |
| 556 | | } |
| 557 | | |
| 558 | | void floppy_drive_set_controller(device_t *img, device_t *controller) |
| 559 | | { |
| 560 | | floppy_drive *drv = get_safe_token( img ); |
| 561 | | drv->controller = controller; |
| 562 | | } |
| 563 | | |
| 564 | | /* ----------------------------------------------------------------------- */ |
| 565 | | DEVICE_START( floppy ) |
| 566 | | { |
| 567 | | floppy_drive *floppy = get_safe_token( device ); |
| 568 | | floppy->config = (const floppy_config*)device->baseconfig().static_config(); |
| 569 | | floppy_drive_init(device); |
| 570 | | |
| 571 | | floppy->drive_id = floppy_get_drive(device); |
| 572 | | floppy->active = FALSE; |
| 573 | | |
| 574 | | /* resolve callbacks */ |
| 575 | | devcb_resolve_write_line(&floppy->out_idx_func, &floppy->config->out_idx_func, device); |
| 576 | | devcb_resolve_read_line(&floppy->in_mon_func, &floppy->config->in_mon_func, device); |
| 577 | | devcb_resolve_write_line(&floppy->out_tk00_func, &floppy->config->out_tk00_func, device); |
| 578 | | devcb_resolve_write_line(&floppy->out_wpt_func, &floppy->config->out_wpt_func, device); |
| 579 | | devcb_resolve_write_line(&floppy->out_rdy_func, &floppy->config->out_rdy_func, device); |
| 580 | | // devcb_resolve_write_line(&floppy->out_dskchg_func, &floppy->config->out_dskchg_func, device); |
| 581 | | |
| 582 | | /* by default we are not write-protected */ |
| 583 | | floppy->wpt = ASSERT_LINE; |
| 584 | | devcb_call_write_line(&floppy->out_wpt_func, floppy->wpt); |
| 585 | | |
| 586 | | /* not at track 0 */ |
| 587 | | floppy->tk00 = ASSERT_LINE; |
| 588 | | devcb_call_write_line(&floppy->out_tk00_func, floppy->tk00); |
| 589 | | |
| 590 | | /* motor off */ |
| 591 | | floppy->mon = ASSERT_LINE; |
| 592 | | |
| 593 | | /* disk changed */ |
| 594 | | floppy->dskchg = CLEAR_LINE; |
| 595 | | // devcb_call_write_line(&floppy->out_dskchg_func, floppy->dskchg); |
| 596 | | } |
| 597 | | |
| 598 | | static int internal_floppy_device_load(device_image_interface *image, int create_format, option_resolution *create_args) |
| 599 | | { |
| 600 | | floperr_t err; |
| 601 | | floppy_drive *flopimg; |
| 602 | | const struct FloppyFormat *floppy_options; |
| 603 | | int floppy_flags, i; |
| 604 | | const char *extension; |
| 605 | | |
| 606 | | /* look up instance data */ |
| 607 | | flopimg = get_safe_token( &image->device() ); |
| 608 | | |
| 609 | | /* figure out the floppy options */ |
| 610 | | floppy_options = ((floppy_config*)image->device().baseconfig().static_config())->formats; |
| 611 | | |
| 612 | | if (image->has_been_created()) |
| 613 | | { |
| 614 | | /* creating an image */ |
| 615 | | assert(create_format >= 0); |
| 616 | | err = floppy_create((void *) image, &image_ioprocs, &floppy_options[create_format], create_args, &flopimg->floppy); |
| 617 | | if (err) |
| 618 | | goto error; |
| 619 | | } |
| 620 | | else |
| 621 | | { |
| 622 | | /* opening an image */ |
| 623 | | floppy_flags = image->is_writable() ? FLOPPY_FLAGS_READWRITE : FLOPPY_FLAGS_READONLY; |
| 624 | | extension = image->filetype(); |
| 625 | | err = floppy_open_choices((void *) image, &image_ioprocs, extension, floppy_options, floppy_flags, &flopimg->floppy); |
| 626 | | if (err) |
| 627 | | goto error; |
| 628 | | } |
| 629 | | if (floppy_callbacks(flopimg->floppy)->get_heads_per_disk && floppy_callbacks(flopimg->floppy)->get_tracks_per_disk) |
| 630 | | { |
| 631 | | floppy_drive_set_geometry_absolute(&image->device(), |
| 632 | | floppy_get_tracks_per_disk(flopimg->floppy), |
| 633 | | floppy_get_heads_per_disk(flopimg->floppy)); |
| 634 | | } |
| 635 | | return IMAGE_INIT_PASS; |
| 636 | | |
| 637 | | error: |
| 638 | | for (i = 0; i < ARRAY_LENGTH(errmap); i++) |
| 639 | | { |
| 640 | | if (err == errmap[i].ferr) |
| 641 | | image->seterror(errmap[i].ierr, errmap[i].message); |
| 642 | | } |
| 643 | | return IMAGE_INIT_FAIL; |
| 644 | | } |
| 645 | | |
| 646 | | static TIMER_CALLBACK( set_wpt ) |
| 647 | | { |
| 648 | | floppy_drive *flopimg = (floppy_drive *)ptr; |
| 649 | | |
| 650 | | flopimg->wpt = param; |
| 651 | | devcb_call_write_line(&flopimg->out_wpt_func, param); |
| 652 | | } |
| 653 | | |
| 654 | | DEVICE_IMAGE_LOAD( floppy ) |
| 655 | | { |
| 656 | | floppy_drive *flopimg; |
| 657 | | int retVal = internal_floppy_device_load(&image, -1, NULL); |
| 658 | | flopimg = get_safe_token( &image.device() ); |
| 659 | | if (retVal==IMAGE_INIT_PASS) { |
| 660 | | /* if we have one of our hacky unload procs, call it */ |
| 661 | | if (flopimg->load_proc) |
| 662 | | flopimg->load_proc(image); |
| 663 | | } |
| 664 | | |
| 665 | | /* push disk halfway into drive */ |
| 666 | | flopimg->wpt = CLEAR_LINE; |
| 667 | | devcb_call_write_line(&flopimg->out_wpt_func, flopimg->wpt); |
| 668 | | |
| 669 | | /* set timer for disk load */ |
| 670 | | int next_wpt; |
| 671 | | |
| 672 | | if (image.is_writable()) |
| 673 | | next_wpt = ASSERT_LINE; |
| 674 | | else |
| 675 | | next_wpt = CLEAR_LINE; |
| 676 | | |
| 677 | | timer_set(image.device().machine, ATTOTIME_IN_MSEC(250), flopimg, next_wpt, set_wpt); |
| 678 | | |
| 679 | | return retVal; |
| 680 | | } |
| 681 | | |
| 682 | | DEVICE_IMAGE_CREATE( floppy ) |
| 683 | | { |
| 684 | | return internal_floppy_device_load(&image, create_format, create_args); |
| 685 | | } |
| 686 | | |
| 687 | | DEVICE_IMAGE_UNLOAD( floppy ) |
| 688 | | { |
| 689 | | floppy_drive *flopimg = get_safe_token( &image.device() ); |
| 690 | | if (flopimg->unload_proc) |
| 691 | | flopimg->unload_proc(image); |
| 692 | | |
| 693 | | floppy_close(flopimg->floppy); |
| 694 | | flopimg->floppy = NULL; |
| 695 | | |
| 696 | | /* disk changed */ |
| 697 | | flopimg->dskchg = CLEAR_LINE; |
| 698 | | //devcb_call_write_line(&flopimg->out_dskchg_func, flopimg->dskchg); |
| 699 | | |
| 700 | | /* pull disk halfway out of drive */ |
| 701 | | flopimg->wpt = CLEAR_LINE; |
| 702 | | devcb_call_write_line(&flopimg->out_wpt_func, flopimg->wpt); |
| 703 | | |
| 704 | | /* set timer for disk eject */ |
| 705 | | timer_set(image.device().machine, ATTOTIME_IN_MSEC(250), flopimg, ASSERT_LINE, set_wpt); |
| 706 | | } |
| 707 | | |
| 708 | | device_t *floppy_get_device(running_machine *machine,int drive) |
| 709 | | { |
| 710 | | switch(drive) { |
| 711 | | case 0 : return machine->device(FLOPPY_0); |
| 712 | | case 1 : return machine->device(FLOPPY_1); |
| 713 | | case 2 : return machine->device(FLOPPY_2); |
| 714 | | case 3 : return machine->device(FLOPPY_3); |
| 715 | | } |
| 716 | | return NULL; |
| 717 | | } |
| 718 | | |
| 719 | | int floppy_get_drive_type(device_t *image) |
| 720 | | { |
| 721 | | floppy_drive *flopimg = get_safe_token( image ); |
| 722 | | return flopimg->floppy_drive_type; |
| 723 | | } |
| 724 | | |
| 725 | | void floppy_set_type(device_t *image,int ftype) |
| 726 | | { |
| 727 | | floppy_drive *flopimg = get_safe_token( image ); |
| 728 | | flopimg->floppy_drive_type = ftype; |
| 729 | | } |
| 730 | | |
| 731 | | device_t *floppy_get_device_by_type(running_machine *machine,int ftype,int drive) |
| 732 | | { |
| 733 | | int i; |
| 734 | | int cnt = 0; |
| 735 | | for (i=0;i<4;i++) { |
| 736 | | device_t *disk = floppy_get_device(machine,i); |
| 737 | | if (floppy_get_drive_type(disk)==ftype) { |
| 738 | | if (cnt==drive) { |
| 739 | | return disk; |
| 740 | | } |
| 741 | | cnt++; |
| 742 | | } |
| 743 | | } |
| 744 | | return NULL; |
| 745 | | } |
| 746 | | |
| 747 | | int floppy_get_drive(device_t *image) |
| 748 | | { |
| 749 | | int drive =0; |
| 750 | | if (strcmp(image->tag(), FLOPPY_0) == 0) drive = 0; |
| 751 | | if (strcmp(image->tag(), FLOPPY_1) == 0) drive = 1; |
| 752 | | if (strcmp(image->tag(), FLOPPY_2) == 0) drive = 2; |
| 753 | | if (strcmp(image->tag(), FLOPPY_3) == 0) drive = 3; |
| 754 | | return drive; |
| 755 | | } |
| 756 | | |
| 757 | | int floppy_get_drive_by_type(device_t *image,int ftype) |
| 758 | | { |
| 759 | | int i,drive =0; |
| 760 | | for (i=0;i<4;i++) { |
| 761 | | device_t *disk = floppy_get_device(image->machine,i); |
| 762 | | if (floppy_get_drive_type(disk)==ftype) { |
| 763 | | if (image==disk) { |
| 764 | | return drive; |
| 765 | | } |
| 766 | | drive++; |
| 767 | | } |
| 768 | | } |
| 769 | | return drive; |
| 770 | | } |
| 771 | | |
| 772 | | int floppy_get_count(running_machine *machine) |
| 773 | | { |
| 774 | | int cnt = 0; |
| 775 | | if (machine->device(FLOPPY_0)) cnt++; |
| 776 | | if (machine->device(FLOPPY_1)) cnt++; |
| 777 | | if (machine->device(FLOPPY_2)) cnt++; |
| 778 | | if (machine->device(FLOPPY_3)) cnt++; |
| 779 | | return cnt; |
| 780 | | } |
| 781 | | |
| 782 | | |
| 783 | | /* drive select 0 */ |
| 784 | | WRITE_LINE_DEVICE_HANDLER( floppy_ds0_w ) |
| 785 | | { |
| 786 | | floppy_drive *drive = get_safe_token(device); |
| 787 | | |
| 788 | | if (state == CLEAR_LINE) |
| 789 | | drive->active = (drive->drive_id == 0); |
| 790 | | } |
| 791 | | |
| 792 | | /* drive select 1 */ |
| 793 | | WRITE_LINE_DEVICE_HANDLER( floppy_ds1_w ) |
| 794 | | { |
| 795 | | floppy_drive *drive = get_safe_token(device); |
| 796 | | |
| 797 | | if (state == CLEAR_LINE) |
| 798 | | drive->active = (drive->drive_id == 1); |
| 799 | | } |
| 800 | | |
| 801 | | /* drive select 2 */ |
| 802 | | WRITE_LINE_DEVICE_HANDLER( floppy_ds2_w ) |
| 803 | | { |
| 804 | | floppy_drive *drive = get_safe_token(device); |
| 805 | | |
| 806 | | if (state == CLEAR_LINE) |
| 807 | | drive->active = (drive->drive_id == 2); |
| 808 | | } |
| 809 | | |
| 810 | | /* drive select 3 */ |
| 811 | | WRITE_LINE_DEVICE_HANDLER( floppy_ds3_w ) |
| 812 | | { |
| 813 | | floppy_drive *drive = get_safe_token(device); |
| 814 | | |
| 815 | | if (state == CLEAR_LINE) |
| 816 | | drive->active = (drive->drive_id == 3); |
| 817 | | } |
| 818 | | |
| 819 | | /* shortcut to write all four ds lines */ |
| 820 | | WRITE8_DEVICE_HANDLER( floppy_ds_w ) |
| 821 | | { |
| 822 | | floppy_ds0_w(device, BIT(data, 0)); |
| 823 | | floppy_ds1_w(device, BIT(data, 1)); |
| 824 | | floppy_ds2_w(device, BIT(data, 2)); |
| 825 | | floppy_ds3_w(device, BIT(data, 3)); |
| 826 | | } |
| 827 | | |
| 828 | | /* motor on, active low */ |
| 829 | | WRITE_LINE_DEVICE_HANDLER( floppy_mon_w ) |
| 830 | | { |
| 831 | | floppy_drive *drive = get_safe_token(device); |
| 832 | | device_image_interface *image = dynamic_cast<device_image_interface *>(device); |
| 833 | | /* force off if there is no attached image */ |
| 834 | | if (!image->exists()) |
| 835 | | state = ASSERT_LINE; |
| 836 | | |
| 837 | | /* off -> on */ |
| 838 | | if (drive->mon && state == CLEAR_LINE) |
| 839 | | { |
| 840 | | drive->idx = 0; |
| 841 | | floppy_drive_index_func(device); |
| 842 | | } |
| 843 | | |
| 844 | | /* on -> off */ |
| 845 | | else if (drive->mon == CLEAR_LINE && state) |
| 846 | | timer_adjust_oneshot((emu_timer*)drive->index_timer, attotime_zero, 0); |
| 847 | | |
| 848 | | drive->mon = state; |
| 849 | | } |
| 850 | | |
| 851 | | /* direction */ |
| 852 | | WRITE_LINE_DEVICE_HANDLER( floppy_drtn_w ) |
| 853 | | { |
| 854 | | floppy_drive *drive = get_safe_token(device); |
| 855 | | drive->drtn = state; |
| 856 | | } |
| 857 | | |
| 858 | | /* write data */ |
| 859 | | WRITE_LINE_DEVICE_HANDLER( floppy_wtd_w ) |
| 860 | | { |
| 861 | | |
| 862 | | } |
| 863 | | |
| 864 | | /* step */ |
| 865 | | WRITE_LINE_DEVICE_HANDLER( floppy_stp_w ) |
| 866 | | { |
| 867 | | floppy_drive *drive = get_safe_token(device); |
| 868 | | |
| 869 | | /* move head one track when going from high to low and write gate is high */ |
| 870 | | if (drive->active && drive->stp && state == CLEAR_LINE && drive->wtg) |
| 871 | | { |
| 872 | | /* move head according to the direction line */ |
| 873 | | if (drive->drtn) |
| 874 | | { |
| 875 | | /* move head outward */ |
| 876 | | if (drive->current_track > 0) |
| 877 | | drive->current_track--; |
| 878 | | |
| 879 | | /* are we at track 0 now? */ |
| 880 | | drive->tk00 = (drive->current_track == 0) ? CLEAR_LINE : ASSERT_LINE; |
| 881 | | } |
| 882 | | else |
| 883 | | { |
| 884 | | /* move head inward */ |
| 885 | | if (drive->current_track < drive->max_track) |
| 886 | | drive->current_track++; |
| 887 | | |
| 888 | | /* we can't be at track 0 here, so reset the line */ |
| 889 | | drive->tk00 = ASSERT_LINE; |
| 890 | | } |
| 891 | | |
| 892 | | /* update track 0 line with new status */ |
| 893 | | devcb_call_write_line(&drive->out_tk00_func, drive->tk00); |
| 894 | | } |
| 895 | | |
| 896 | | drive->stp = state; |
| 897 | | } |
| 898 | | |
| 899 | | /* write gate */ |
| 900 | | WRITE_LINE_DEVICE_HANDLER( floppy_wtg_w ) |
| 901 | | { |
| 902 | | floppy_drive *drive = get_safe_token(device); |
| 903 | | drive->wtg = state; |
| 904 | | } |
| 905 | | |
| 906 | | /* write protect signal, active low */ |
| 907 | | READ_LINE_DEVICE_HANDLER( floppy_wpt_r ) |
| 908 | | { |
| 909 | | floppy_drive *drive = get_safe_token(device); |
| 910 | | return drive->wpt; |
| 911 | | } |
| 912 | | |
| 913 | | /* track 0 detect */ |
| 914 | | READ_LINE_DEVICE_HANDLER( floppy_tk00_r ) |
| 915 | | { |
| 916 | | floppy_drive *drive = get_safe_token(device); |
| 917 | | return drive->tk00; |
| 918 | | } |
| 919 | | |
| 920 | | /* disk changed */ |
| 921 | | READ_LINE_DEVICE_HANDLER( floppy_dskchg_r ) |
| 922 | | { |
| 923 | | floppy_drive *drive = get_safe_token(device); |
| 924 | | return drive->dskchg; |
| 925 | | } |
| 926 | | |
| 927 | | /* 2-sided disk */ |
| 928 | | READ_LINE_DEVICE_HANDLER( floppy_twosid_r ) |
| 929 | | { |
| 930 | | floppy_drive *drive = get_safe_token(device); |
| 931 | | |
| 932 | | if (drive->floppy == NULL) |
| 933 | | return ASSERT_LINE; |
| 934 | | else |
| 935 | | return !floppy_get_heads_per_disk(drive->floppy); |
| 936 | | } |
| 937 | | |
| 938 | | /*------------------------------------------------- |
| 939 | | DEVICE_IMAGE_SOFTLIST_LOAD(floppy) |
| 940 | | -------------------------------------------------*/ |
| 941 | | static DEVICE_IMAGE_SOFTLIST_LOAD(floppy) |
| 942 | | { |
| 943 | | return image.load_software(swlist, swname, start_entry); |
| 944 | | } |
| 945 | | |
| 946 | | /************************************* |
| 947 | | * |
| 948 | | * Device specification function |
| 949 | | * |
| 950 | | *************************************/ |
| 951 | | /*------------------------------------------------- |
| 952 | | safe_strcpy - hack |
| 953 | | -------------------------------------------------*/ |
| 954 | | |
| 955 | | static void safe_strcpy(char *dst, const char *src) |
| 956 | | { |
| 957 | | strcpy(dst, src ? src : ""); |
| 958 | | } |
| 959 | | |
| 960 | | DEVICE_GET_INFO(floppy) |
| 961 | | { |
| 962 | | switch( state ) |
| 963 | | { |
| 964 | | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 965 | | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(floppy_drive); break; |
| 966 | | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; |
| 967 | | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_FLOPPY; break; |
| 968 | | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 969 | | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break; |
| 970 | | case DEVINFO_INT_IMAGE_CREATABLE: |
| 971 | | { |
| 972 | | int cnt = 0; |
| 973 | | if ( device && device->static_config() ) |
| 974 | | { |
| 975 | | const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats; |
| 976 | | int i; |
| 977 | | for ( i = 0; floppy_options[i].construct; i++ ) { |
| 978 | | if(floppy_options[i].param_guidelines) cnt++; |
| 979 | | } |
| 980 | | } |
| 981 | | info->i = (cnt>0) ? 1 : 0; |
| 982 | | } |
| 983 | | break; |
| 984 | | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 985 | | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(floppy); break; |
| 986 | | case DEVINFO_FCT_IMAGE_CREATE: info->f = (genf *) DEVICE_IMAGE_CREATE_NAME(floppy); break; |
| 987 | | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(floppy); break; |
| 988 | | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(floppy); break; |
| 989 | | case DEVINFO_FCT_IMAGE_SOFTLIST_LOAD: info->f = (genf *) DEVICE_IMAGE_SOFTLIST_LOAD_NAME(floppy); break; |
| 990 | | case DEVINFO_PTR_IMAGE_CREATE_OPTGUIDE: info->p = (void *)floppy_option_guide; break; |
| 991 | | case DEVINFO_INT_IMAGE_CREATE_OPTCOUNT: |
| 992 | | { |
| 993 | | const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats; |
| 994 | | int count; |
| 995 | | for (count = 0; floppy_options[count].construct; count++) |
| 996 | | ; |
| 997 | | info->i = count; |
| 998 | | break; |
| 999 | | } |
| 1000 | | |
| 1001 | | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 1002 | | case DEVINFO_STR_NAME: strcpy(info->s, "Floppy Disk"); break; |
| 1003 | | case DEVINFO_STR_FAMILY: strcpy(info->s, "Floppy Disk"); break; |
| 1004 | | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 1005 | | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: |
| 1006 | | if ( device && device->static_config() ) |
| 1007 | | { |
| 1008 | | const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats; |
| 1009 | | int i; |
| 1010 | | /* set up a temporary string */ |
| 1011 | | info->s[0] = '\0'; |
| 1012 | | for ( i = 0; floppy_options[i].construct; i++ ) |
| 1013 | | image_specify_extension( info->s, 256, floppy_options[i].extensions ); |
| 1014 | | } |
| 1015 | | break; |
| 1016 | | case DEVINFO_STR_IMAGE_INTERFACE: |
| 1017 | | if ( device && device->static_config() && ((floppy_config *)device->static_config())->interface) |
| 1018 | | { |
| 1019 | | strcpy(info->s, ((floppy_config *)device->static_config())->interface ); |
| 1020 | | } |
| 1021 | | break; |
| 1022 | | default: |
| 1023 | | { |
| 1024 | | if ( device && device->static_config() ) |
| 1025 | | { |
| 1026 | | const struct FloppyFormat *floppy_options = ((floppy_config*)device->static_config())->formats; |
| 1027 | | if ((state >= DEVINFO_PTR_IMAGE_CREATE_OPTSPEC) && (state < DEVINFO_PTR_IMAGE_CREATE_OPTSPEC + DEVINFO_IMAGE_CREATE_OPTMAX)) { |
| 1028 | | info->p = (void *) floppy_options[state - DEVINFO_PTR_IMAGE_CREATE_OPTSPEC].param_guidelines; |
| 1029 | | } else if ((state >= DEVINFO_STR_IMAGE_CREATE_OPTNAME) && (state < DEVINFO_STR_IMAGE_CREATE_OPTNAME + DEVINFO_IMAGE_CREATE_OPTMAX)) { |
| 1030 | | safe_strcpy(info->s,floppy_options[state - DEVINFO_STR_IMAGE_CREATE_OPTNAME].name); |
| 1031 | | } |
| 1032 | | else if ((state >= DEVINFO_STR_IMAGE_CREATE_OPTDESC) && (state < DEVINFO_STR_IMAGE_CREATE_OPTDESC + DEVINFO_IMAGE_CREATE_OPTMAX)) { |
| 1033 | | safe_strcpy(info->s,floppy_options[state - DEVINFO_STR_IMAGE_CREATE_OPTDESC].description); |
| 1034 | | } |
| 1035 | | else if ((state >= DEVINFO_STR_IMAGE_CREATE_OPTEXTS) && (state < DEVINFO_STR_IMAGE_CREATE_OPTEXTS + DEVINFO_IMAGE_CREATE_OPTMAX)) { |
| 1036 | | safe_strcpy(info->s,floppy_options[state - DEVINFO_STR_IMAGE_CREATE_OPTEXTS].extensions); |
| 1037 | | } |
| 1038 | | } |
| 1039 | | } |
| 1040 | | |
| 1041 | | break; |
| 1042 | | } |
| 1043 | | } |
| 1044 | | |
| 1045 | | DEFINE_LEGACY_IMAGE_DEVICE(FLOPPY, floppy); |
src/mess/devices/flopdrv.h
| r9927 | r9928 | |
| 1 | | /* flopdrv provides simple emulation of a disc drive */ |
| 2 | | /* the 8271, upd765 and wd179x use this */ |
| 3 | | |
| 4 | | #ifndef __FLOPDRV_H__ |
| 5 | | #define __FLOPDRV_H__ |
| 6 | | |
| 7 | | #include "devcb.h" |
| 8 | | #include "image.h" |
| 9 | | #include "formats/flopimg.h" |
| 10 | | |
| 11 | | #define FLOPPY_TYPE_REGULAR 0 |
| 12 | | #define FLOPPY_TYPE_APPLE 1 |
| 13 | | #define FLOPPY_TYPE_SONY 2 |
| 14 | | |
| 15 | | #define FLOPPY_DRIVE_2_8_INCH 1 |
| 16 | | #define FLOPPY_DRIVE_3_INCH 2 |
| 17 | | #define FLOPPY_DRIVE_3_5_INCH 3 |
| 18 | | #define FLOPPY_DRIVE_5_25_INCH 4 |
| 19 | | #define FLOPPY_DRIVE_8_INCH 5 |
| 20 | | |
| 21 | | // Maximum supported density |
| 22 | | #define FLOPPY_DRIVE_SD 1 |
| 23 | | #define FLOPPY_DRIVE_DD 2 |
| 24 | | #define FLOPPY_DRIVE_QD 3 |
| 25 | | #define FLOPPY_DRIVE_HD 4 |
| 26 | | #define FLOPPY_DRIVE_ED 5 |
| 27 | | |
| 28 | | #define FLOPPY_STANDARD_3_SSDD { FLOPPY_DRIVE_3_INCH, 1, 42, FLOPPY_DRIVE_DD } |
| 29 | | #define FLOPPY_STANDARD_3_DSDD { FLOPPY_DRIVE_3_INCH, 2, 42, FLOPPY_DRIVE_DD } |
| 30 | | #define FLOPPY_STANDARD_3_5_DSDD { FLOPPY_DRIVE_3_5_INCH, 2, 83, FLOPPY_DRIVE_DD } |
| 31 | | #define FLOPPY_STANDARD_3_5_DSHD { FLOPPY_DRIVE_3_5_INCH, 2, 83, FLOPPY_DRIVE_HD } |
| 32 | | #define FLOPPY_STANDARD_5_25_SSSD_35 { FLOPPY_DRIVE_5_25_INCH, 1, 37, FLOPPY_DRIVE_SD } |
| 33 | | #define FLOPPY_STANDARD_5_25_DSSD_35 { FLOPPY_DRIVE_5_25_INCH, 2, 37, FLOPPY_DRIVE_SD } |
| 34 | | #define FLOPPY_STANDARD_5_25_SSSD { FLOPPY_DRIVE_5_25_INCH, 1, 42, FLOPPY_DRIVE_SD } |
| 35 | | #define FLOPPY_STANDARD_5_25_DSSD { FLOPPY_DRIVE_5_25_INCH, 2, 42, FLOPPY_DRIVE_SD } |
| 36 | | #define FLOPPY_STANDARD_5_25_SSDD_40 { FLOPPY_DRIVE_5_25_INCH, 1, 42, FLOPPY_DRIVE_DD } |
| 37 | | #define FLOPPY_STANDARD_5_25_DSDD_40 { FLOPPY_DRIVE_5_25_INCH, 2, 42, FLOPPY_DRIVE_DD } |
| 38 | | #define FLOPPY_STANDARD_5_25_SSDD { FLOPPY_DRIVE_5_25_INCH, 1, 83, FLOPPY_DRIVE_DD } |
| 39 | | #define FLOPPY_STANDARD_5_25_DSDD { FLOPPY_DRIVE_5_25_INCH, 2, 83, FLOPPY_DRIVE_DD } |
| 40 | | #define FLOPPY_STANDARD_5_25_DSQD { FLOPPY_DRIVE_5_25_INCH, 2, 83, FLOPPY_DRIVE_QD } |
| 41 | | #define FLOPPY_STANDARD_5_25_DSHD { FLOPPY_DRIVE_5_25_INCH, 2, 83, FLOPPY_DRIVE_HD } |
| 42 | | #define FLOPPY_STANDARD_8_SSSD { FLOPPY_DRIVE_8_INCH, 1, 77, FLOPPY_DRIVE_SD } |
| 43 | | #define FLOPPY_STANDARD_8_DSSD { FLOPPY_DRIVE_8_INCH, 2, 77, FLOPPY_DRIVE_SD } |
| 44 | | #define FLOPPY_STANDARD_8_DSDD { FLOPPY_DRIVE_8_INCH, 2, 77, FLOPPY_DRIVE_DD } |
| 45 | | |
| 46 | | /*************************************************************************** |
| 47 | | TYPE DEFINITIONS |
| 48 | | ***************************************************************************/ |
| 49 | | typedef struct floppy_type_t floppy_type; |
| 50 | | struct floppy_type_t |
| 51 | | { |
| 52 | | UINT8 media_size; |
| 53 | | UINT8 head_number; |
| 54 | | UINT8 max_track_number; |
| 55 | | UINT8 max_density; |
| 56 | | }; |
| 57 | | |
| 58 | | typedef struct floppy_config_t floppy_config; |
| 59 | | struct floppy_config_t |
| 60 | | { |
| 61 | | devcb_write_line out_idx_func; /* index */ |
| 62 | | devcb_read_line in_mon_func; /* motor on */ |
| 63 | | devcb_write_line out_tk00_func; /* track 00 */ |
| 64 | | devcb_write_line out_wpt_func; /* write protect */ |
| 65 | | devcb_write_line out_rdy_func; /* ready */ |
| 66 | | // devcb_write_line out_dskchg_func; /* disk changed */ |
| 67 | | |
| 68 | | floppy_type_t floppy_type; |
| 69 | | const struct FloppyFormat *formats; |
| 70 | | const char *interface; |
| 71 | | }; |
| 72 | | |
| 73 | | /* sector has a deleted data address mark */ |
| 74 | | #define ID_FLAG_DELETED_DATA 0x0001 |
| 75 | | /* CRC error in id field */ |
| 76 | | #define ID_FLAG_CRC_ERROR_IN_ID_FIELD 0x0002 |
| 77 | | /* CRC error in data field */ |
| 78 | | #define ID_FLAG_CRC_ERROR_IN_DATA_FIELD 0x0004 |
| 79 | | |
| 80 | | typedef struct chrn_id |
| 81 | | { |
| 82 | | unsigned char C; |
| 83 | | unsigned char H; |
| 84 | | unsigned char R; |
| 85 | | unsigned char N; |
| 86 | | int data_id; // id for read/write data command |
| 87 | | unsigned long flags; |
| 88 | | } chrn_id; |
| 89 | | |
| 90 | | /* set if drive is ready */ |
| 91 | | #define FLOPPY_DRIVE_READY 0x0010 |
| 92 | | /* set if index has just occured */ |
| 93 | | #define FLOPPY_DRIVE_INDEX 0x0020 |
| 94 | | |
| 95 | | /* a callback which will be executed if the ready state of the drive changes e.g. not ready->ready, ready->not ready */ |
| 96 | | void floppy_drive_set_ready_state_change_callback(device_t *img, void (*callback)(device_t *controller,device_t *img, int state)); |
| 97 | | |
| 98 | | void floppy_drive_set_index_pulse_callback(device_t *img, void (*callback)(device_t *controller,device_t *image, int state)); |
| 99 | | |
| 100 | | /* set flag state */ |
| 101 | | int floppy_drive_get_flag_state(device_t *img, int flag); |
| 102 | | /* get flag state */ |
| 103 | | void floppy_drive_set_flag_state(device_t *img, int flag, int state); |
| 104 | | /* get current physical track drive is on */ |
| 105 | | int floppy_drive_get_current_track(device_t *img); |
| 106 | | /* get current physical track size */ |
| 107 | | UINT64 floppy_drive_get_current_track_size(device_t *img, int head); |
| 108 | | |
| 109 | | /* get next id from track, 1 if got a id, 0 if no id was got */ |
| 110 | | int floppy_drive_get_next_id(device_t *img, int side, chrn_id *); |
| 111 | | /* set ready state of drive. If flag == 1, set ready state only if drive present, |
| 112 | | disk is in drive, and motor is on. Otherwise set ready state to the state passed */ |
| 113 | | void floppy_drive_set_ready_state(device_t *img, int state, int flag); |
| 114 | | |
| 115 | | /* seek up or down */ |
| 116 | | void floppy_drive_seek(device_t *img, signed int signed_tracks); |
| 117 | | |
| 118 | | void floppy_drive_read_track_data_info_buffer(device_t *img, int side, void *ptr, int *length ); |
| 119 | | void floppy_drive_write_track_data_info_buffer(device_t *img, int side, const void *ptr, int *length ); |
| 120 | | void floppy_drive_format_sector(device_t *img, int side, int sector_index, int c, int h, int r, int n, int filler); |
| 121 | | void floppy_drive_read_sector_data(device_t *img, int side, int index1, void *pBuffer, int length); |
| 122 | | void floppy_drive_write_sector_data(device_t *img, int side, int index1, const void *pBuffer, int length, int ddam); |
| 123 | | |
| 124 | | /* set motor speed to get correct index pulses |
| 125 | | standard RPM are 300 RPM (common) and 360 RPM |
| 126 | | Note: this actually only works for soft sectored disks: one index pulse per |
| 127 | | track. |
| 128 | | */ |
| 129 | | void floppy_drive_set_rpm(device_t *image, float rpm); |
| 130 | | |
| 131 | | void floppy_drive_set_controller(device_t *img, device_t *controller); |
| 132 | | |
| 133 | | floppy_image *flopimg_get_image(device_t *image); |
| 134 | | |
| 135 | | /* hack for apple II; replace this when we think of something better */ |
| 136 | | void floppy_install_unload_proc(device_t *image, void (*proc)(device_image_interface &image)); |
| 137 | | |
| 138 | | void floppy_install_load_proc(device_t *image, void (*proc)(device_image_interface &image)); |
| 139 | | |
| 140 | | device_t *floppy_get_device(running_machine *machine,int drive); |
| 141 | | device_t *floppy_get_device_by_type(running_machine *machine,int ftype,int drive); |
| 142 | | int floppy_get_drive_type(device_t *image); |
| 143 | | void floppy_set_type(device_t *image,int ftype); |
| 144 | | int floppy_get_count(running_machine *machine); |
| 145 | | |
| 146 | | int floppy_get_drive(device_t *image); |
| 147 | | int floppy_get_drive_by_type(device_t *image,int ftype); |
| 148 | | |
| 149 | | void *flopimg_get_custom_data(device_t *image); |
| 150 | | void flopimg_alloc_custom_data(device_t *image,void *custom); |
| 151 | | |
| 152 | | void floppy_drive_set_geometry(device_t *img, floppy_type_t type); |
| 153 | | |
| 154 | | /* drive select lines */ |
| 155 | | WRITE_LINE_DEVICE_HANDLER( floppy_ds0_w ); |
| 156 | | WRITE_LINE_DEVICE_HANDLER( floppy_ds1_w ); |
| 157 | | WRITE_LINE_DEVICE_HANDLER( floppy_ds2_w ); |
| 158 | | WRITE_LINE_DEVICE_HANDLER( floppy_ds3_w ); |
| 159 | | WRITE8_DEVICE_HANDLER( floppy_ds_w ); |
| 160 | | |
| 161 | | WRITE_LINE_DEVICE_HANDLER( floppy_mon_w ); |
| 162 | | WRITE_LINE_DEVICE_HANDLER( floppy_drtn_w ); |
| 163 | | WRITE_LINE_DEVICE_HANDLER( floppy_stp_w ); |
| 164 | | WRITE_LINE_DEVICE_HANDLER( floppy_wtd_w ); |
| 165 | | WRITE_LINE_DEVICE_HANDLER( floppy_wtg_w ); |
| 166 | | |
| 167 | | /* write-protect */ |
| 168 | | READ_LINE_DEVICE_HANDLER( floppy_wpt_r ); |
| 169 | | |
| 170 | | /* track 0 detect */ |
| 171 | | READ_LINE_DEVICE_HANDLER( floppy_tk00_r ); |
| 172 | | |
| 173 | | /* disk changed */ |
| 174 | | READ_LINE_DEVICE_HANDLER( floppy_dskchg_r ); |
| 175 | | |
| 176 | | /* 2-sided disk */ |
| 177 | | READ_LINE_DEVICE_HANDLER( floppy_twosid_r ); |
| 178 | | |
| 179 | | DECLARE_LEGACY_IMAGE_DEVICE(FLOPPY, floppy); |
| 180 | | |
| 181 | | extern DEVICE_START( floppy ); |
| 182 | | extern DEVICE_IMAGE_LOAD( floppy ); |
| 183 | | extern DEVICE_IMAGE_CREATE( floppy ); |
| 184 | | extern DEVICE_IMAGE_UNLOAD( floppy ); |
| 185 | | |
| 186 | | /*************************************************************************** |
| 187 | | DEVICE CONFIGURATION MACROS |
| 188 | | ***************************************************************************/ |
| 189 | | #define FLOPPY_0 "floppy0" |
| 190 | | #define FLOPPY_1 "floppy1" |
| 191 | | #define FLOPPY_2 "floppy2" |
| 192 | | #define FLOPPY_3 "floppy3" |
| 193 | | |
| 194 | | |
| 195 | | #define MCFG_FLOPPY_DRIVE_ADD(_tag, _config) \ |
| 196 | | MCFG_DEVICE_ADD(_tag, FLOPPY, 0) \ |
| 197 | | MCFG_DEVICE_CONFIG(_config) |
| 198 | | |
| 199 | | #define MCFG_FLOPPY_DRIVE_MODIFY(_tag, _config) \ |
| 200 | | MCFG_DEVICE_MODIFY(_tag) \ |
| 201 | | MCFG_DEVICE_CONFIG(_config) |
| 202 | | |
| 203 | | #define MCFG_FLOPPY_4_DRIVES_ADD(_config) \ |
| 204 | | MCFG_DEVICE_ADD(FLOPPY_0, FLOPPY, 0) \ |
| 205 | | MCFG_DEVICE_CONFIG(_config) \ |
| 206 | | MCFG_DEVICE_ADD(FLOPPY_1, FLOPPY, 0) \ |
| 207 | | MCFG_DEVICE_CONFIG(_config) \ |
| 208 | | MCFG_DEVICE_ADD(FLOPPY_2, FLOPPY, 0) \ |
| 209 | | MCFG_DEVICE_CONFIG(_config) \ |
| 210 | | MCFG_DEVICE_ADD(FLOPPY_3, FLOPPY, 0) \ |
| 211 | | MCFG_DEVICE_CONFIG(_config) |
| 212 | | |
| 213 | | #define MCFG_FLOPPY_4_DRIVES_MODIFY(_config) \ |
| 214 | | MCFG_DEVICE_MODIFY(FLOPPY_0) \ |
| 215 | | MCFG_DEVICE_CONFIG(_config) \ |
| 216 | | MCFG_DEVICE_MODIFY(FLOPPY_1) \ |
| 217 | | MCFG_DEVICE_CONFIG(_config) \ |
| 218 | | MCFG_DEVICE_MODIFY(FLOPPY_2) \ |
| 219 | | MCFG_DEVICE_CONFIG(_config) \ |
| 220 | | MCFG_DEVICE_MODIFY(FLOPPY_3) \ |
| 221 | | MCFG_DEVICE_CONFIG(_config) |
| 222 | | |
| 223 | | #define MCFG_FLOPPY_4_DRIVES_REMOVE() \ |
| 224 | | MCFG_DEVICE_REMOVE(FLOPPY_0) \ |
| 225 | | MCFG_DEVICE_REMOVE(FLOPPY_1) \ |
| 226 | | MCFG_DEVICE_REMOVE(FLOPPY_2) \ |
| 227 | | MCFG_DEVICE_REMOVE(FLOPPY_3) |
| 228 | | |
| 229 | | #define MCFG_FLOPPY_2_DRIVES_ADD(_config) \ |
| 230 | | MCFG_DEVICE_ADD(FLOPPY_0, FLOPPY, 0) \ |
| 231 | | MCFG_DEVICE_CONFIG(_config) \ |
| 232 | | MCFG_DEVICE_ADD(FLOPPY_1, FLOPPY, 0) \ |
| 233 | | MCFG_DEVICE_CONFIG(_config) |
| 234 | | |
| 235 | | #define MCFG_FLOPPY_2_DRIVES_MODIFY(_config) \ |
| 236 | | MCFG_DEVICE_MODIFY(FLOPPY_0) \ |
| 237 | | MCFG_DEVICE_CONFIG(_config) \ |
| 238 | | MCFG_DEVICE_MODIFY(FLOPPY_1) \ |
| 239 | | MCFG_DEVICE_CONFIG(_config) |
| 240 | | |
| 241 | | #define MCFG_FLOPPY_2_DRIVES_REMOVE() \ |
| 242 | | MCFG_DEVICE_REMOVE(FLOPPY_0) \ |
| 243 | | MCFG_DEVICE_REMOVE(FLOPPY_1) |
| 244 | | |
| 245 | | #endif /* __FLOPDRV_H__ */ |
src/mess/devices/cartslot.c
| r9927 | r9928 | |
| 1 | | /********************************************************************** |
| 2 | | |
| 3 | | cartslot.c |
| 4 | | |
| 5 | | Cartridge device |
| 6 | | |
| 7 | | **********************************************************************/ |
| 8 | | |
| 9 | | #include <ctype.h> |
| 10 | | #include "emu.h" |
| 11 | | #include "cartslot.h" |
| 12 | | #include "multcart.h" |
| 13 | | |
| 14 | | /*************************************************************************** |
| 15 | | CONSTANTS |
| 16 | | ***************************************************************************/ |
| 17 | | |
| 18 | | enum _process_mode |
| 19 | | { |
| 20 | | PROCESS_CLEAR, |
| 21 | | PROCESS_LOAD |
| 22 | | }; |
| 23 | | typedef enum _process_mode process_mode; |
| 24 | | |
| 25 | | |
| 26 | | /*************************************************************************** |
| 27 | | INLINE FUNCTIONS |
| 28 | | ***************************************************************************/ |
| 29 | | |
| 30 | | INLINE cartslot_t *get_token(device_t *device) |
| 31 | | { |
| 32 | | assert(device != NULL); |
| 33 | | assert(device->type() == CARTSLOT); |
| 34 | | return (cartslot_t *) downcast<legacy_device_base *>(device)->token(); |
| 35 | | } |
| 36 | | |
| 37 | | |
| 38 | | INLINE const cartslot_config *get_config(device_t *device) |
| 39 | | { |
| 40 | | assert(device != NULL); |
| 41 | | assert(device->type() == CARTSLOT); |
| 42 | | return (const cartslot_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config(); |
| 43 | | } |
| 44 | | |
| 45 | | INLINE const cartslot_config *get_config_dev(const device_config *device) |
| 46 | | { |
| 47 | | assert(device != NULL); |
| 48 | | assert(device->type() == CARTSLOT); |
| 49 | | return (const cartslot_config *)downcast<const legacy_device_config_base *>(device)->inline_config(); |
| 50 | | } |
| 51 | | |
| 52 | | /*************************************************************************** |
| 53 | | IMPLEMENTATION |
| 54 | | ***************************************************************************/ |
| 55 | | |
| 56 | | /*------------------------------------------------- |
| 57 | | load_cartridge |
| 58 | | -------------------------------------------------*/ |
| 59 | | |
| 60 | | static int load_cartridge(device_image_interface *image, const rom_entry *romrgn, const rom_entry *roment, process_mode mode) |
| 61 | | { |
| 62 | | const char *region; |
| 63 | | const char *type; |
| 64 | | UINT32 flags; |
| 65 | | offs_t offset, length, read_length, pos = 0, len; |
| 66 | | UINT8 *ptr; |
| 67 | | UINT8 clear_val; |
| 68 | | int datawidth, littleendian, i, j; |
| 69 | | device_t *cpu; |
| 70 | | |
| 71 | | region = ROMREGION_GETTAG(romrgn); |
| 72 | | offset = ROM_GETOFFSET(roment); |
| 73 | | length = ROM_GETLENGTH(roment); |
| 74 | | flags = ROM_GETFLAGS(roment); |
| 75 | | ptr = ((UINT8 *) image->device().machine->region(region)->base()) + offset; |
| 76 | | |
| 77 | | if (mode == PROCESS_LOAD) |
| 78 | | { |
| 79 | | if (image->software_entry() == NULL) |
| 80 | | { |
| 81 | | /* must this be full size */ |
| 82 | | if (flags & ROM_FULLSIZE) |
| 83 | | { |
| 84 | | if (image->length() != length) |
| 85 | | return IMAGE_INIT_FAIL; |
| 86 | | } |
| 87 | | |
| 88 | | /* read the ROM */ |
| 89 | | pos = read_length = image->fread(ptr, length); |
| 90 | | |
| 91 | | /* reset the ROM to the initial point. */ |
| 92 | | /* eventually, we could add a flag to allow the ROM to continue instead of restarting whenever a new cart region is present */ |
| 93 | | image->fseek(0, SEEK_SET); |
| 94 | | } |
| 95 | | else |
| 96 | | { |
| 97 | | /* must this be full size */ |
| 98 | | if (flags & ROM_FULLSIZE) |
| 99 | | { |
| 100 | | if (image->get_software_region_length("rom") != length) |
| 101 | | return IMAGE_INIT_FAIL; |
| 102 | | } |
| 103 | | |
| 104 | | /* read the ROM */ |
| 105 | | pos = read_length = image->get_software_region_length("rom"); |
| 106 | | memcpy(ptr, image->get_software_region("rom"), read_length); |
| 107 | | } |
| 108 | | |
| 109 | | /* do we need to mirror the ROM? */ |
| 110 | | if (flags & ROM_MIRROR) |
| 111 | | { |
| 112 | | while(pos < length) |
| 113 | | { |
| 114 | | len = MIN(read_length, length - pos); |
| 115 | | memcpy(ptr + pos, ptr, len); |
| 116 | | pos += len; |
| 117 | | } |
| 118 | | } |
| 119 | | |
| 120 | | /* postprocess this region */ |
| 121 | | type = ROMREGION_GETTAG(romrgn); |
| 122 | | littleendian = ROMREGION_ISLITTLEENDIAN(romrgn); |
| 123 | | datawidth = ROMREGION_GETWIDTH(romrgn) / 8; |
| 124 | | |
| 125 | | /* if the region is inverted, do that now */ |
| 126 | | device_memory_interface *memory; |
| 127 | | cpu = image->device().machine->device(type); |
| 128 | | if (cpu!=NULL && cpu->interface(memory)) |
| 129 | | { |
| 130 | | datawidth = device_memory(cpu)->space_config(AS_PROGRAM)->m_databus_width / 8; |
| 131 | | littleendian = (device_memory(cpu)->space_config()->m_endianness == ENDIANNESS_LITTLE); |
| 132 | | } |
| 133 | | |
| 134 | | /* swap the endianness if we need to */ |
| 135 | | #ifdef LSB_FIRST |
| 136 | | if (datawidth > 1 && !littleendian) |
| 137 | | #else |
| 138 | | if (datawidth > 1 && littleendian) |
| 139 | | #endif |
| 140 | | { |
| 141 | | for (i = 0; i < length; i += datawidth) |
| 142 | | { |
| 143 | | UINT8 temp[8]; |
| 144 | | memcpy(temp, &ptr[i], datawidth); |
| 145 | | for (j = datawidth - 1; j >= 0; j--) |
| 146 | | ptr[i + j] = temp[datawidth - 1 - j]; |
| 147 | | } |
| 148 | | } |
| 149 | | } |
| 150 | | |
| 151 | | /* clear out anything that remains */ |
| 152 | | if (!(flags & ROM_NOCLEAR)) |
| 153 | | { |
| 154 | | clear_val = (flags & ROM_FILL_FF) ? 0xFF : 0x00; |
| 155 | | memset(ptr + pos, clear_val, length - pos); |
| 156 | | } |
| 157 | | return IMAGE_INIT_PASS; |
| 158 | | } |
| 159 | | |
| 160 | | |
| 161 | | |
| 162 | | /*------------------------------------------------- |
| 163 | | process_cartridge |
| 164 | | -------------------------------------------------*/ |
| 165 | | |
| 166 | | static int process_cartridge(device_image_interface *image, process_mode mode) |
| 167 | | { |
| 168 | | const rom_source *source; |
| 169 | | const rom_entry *romrgn, *roment; |
| 170 | | int result = 0; |
| 171 | | |
| 172 | | for (source = rom_first_source(*image->device().machine->config); source != NULL; source = rom_next_source(*source)) |
| 173 | | { |
| 174 | | for (romrgn = rom_first_region(*source); romrgn != NULL; romrgn = rom_next_region(romrgn)) |
| 175 | | { |
| 176 | | roment = romrgn + 1; |
| 177 | | while(!ROMENTRY_ISREGIONEND(roment)) |
| 178 | | { |
| 179 | | if (ROMENTRY_GETTYPE(roment) == ROMENTRYTYPE_CARTRIDGE) |
| 180 | | { |
| 181 | | if (strcmp(roment->_hashdata,image->device().tag())==0) |
| 182 | | { |
| 183 | | result |= load_cartridge(image, romrgn, roment, mode); |
| 184 | | |
| 185 | | /* if loading failed in any cart region, stop loading */ |
| 186 | | if (result) |
| 187 | | return result; |
| 188 | | } |
| 189 | | } |
| 190 | | roment++; |
| 191 | | } |
| 192 | | } |
| 193 | | } |
| 194 | | |
| 195 | | return IMAGE_INIT_PASS; |
| 196 | | } |
| 197 | | |
| 198 | | |
| 199 | | /*------------------------------------------------- |
| 200 | | cartslot_get_pcb |
| 201 | | -------------------------------------------------*/ |
| 202 | | |
| 203 | | device_t *cartslot_get_pcb(device_t *device) |
| 204 | | { |
| 205 | | cartslot_t *cart = get_token(device); |
| 206 | | return cart->pcb_device; |
| 207 | | } |
| 208 | | |
| 209 | | |
| 210 | | /*------------------------------------------------- |
| 211 | | cartslot_get_socket |
| 212 | | -------------------------------------------------*/ |
| 213 | | |
| 214 | | void *cartslot_get_socket(device_t *device, const char *socket_name) |
| 215 | | { |
| 216 | | cartslot_t *cart = get_token(device); |
| 217 | | device_image_interface *image = dynamic_cast<device_image_interface *>(device); |
| 218 | | void *result = NULL; |
| 219 | | |
| 220 | | if (cart->mc != NULL) |
| 221 | | { |
| 222 | | const multicart_socket *socket; |
| 223 | | for (socket = cart->mc->sockets; socket != NULL; socket = socket->next) |
| 224 | | { |
| 225 | | if (!strcmp(socket->id, socket_name)) |
| 226 | | break; |
| 227 | | } |
| 228 | | result = socket ? socket->ptr : NULL; |
| 229 | | } |
| 230 | | else if (socket_name[0] == '\0') |
| 231 | | { |
| 232 | | result = image->ptr(); |
| 233 | | } |
| 234 | | return result; |
| 235 | | } |
| 236 | | |
| 237 | | /*------------------------------------------------- |
| 238 | | cartslot_get_resource_length |
| 239 | | -------------------------------------------------*/ |
| 240 | | |
| 241 | | int cartslot_get_resource_length(device_t *device, const char *socket_name) |
| 242 | | { |
| 243 | | cartslot_t *cart = get_token(device); |
| 244 | | int result = 0; |
| 245 | | |
| 246 | | if (cart->mc != NULL) |
| 247 | | { |
| 248 | | const multicart_socket *socket; |
| 249 | | |
| 250 | | for (socket = cart->mc->sockets; socket != NULL; socket = socket->next) |
| 251 | | { |
| 252 | | if (!strcmp(socket->id, socket_name)) { |
| 253 | | break; |
| 254 | | } |
| 255 | | } |
| 256 | | if (socket != NULL) |
| 257 | | result = socket->resource->length; |
| 258 | | } |
| 259 | | else |
| 260 | | result = 0; |
| 261 | | |
| 262 | | return result; |
| 263 | | } |
| 264 | | |
| 265 | | |
| 266 | | /*------------------------------------------------- |
| 267 | | DEVICE_START( cartslot ) |
| 268 | | -------------------------------------------------*/ |
| 269 | | |
| 270 | | static DEVICE_START( cartslot ) |
| 271 | | { |
| 272 | | cartslot_t *cart = get_token(device); |
| 273 | | const cartslot_config *config = get_config(device); |
| 274 | | |
| 275 | | /* if this cartridge has a custom DEVICE_START, use it */ |
| 276 | | if (config->device_start != NULL) |
| 277 | | { |
| 278 | | (*config->device_start)(device); |
| 279 | | goto done; |
| 280 | | } |
| 281 | | |
| 282 | | /* find the PCB (if there is one) */ |
| 283 | | cart->pcb_device = device->subdevice(TAG_PCB); |
| 284 | | |
| 285 | | done: |
| 286 | | return; |
| 287 | | } |
| 288 | | |
| 289 | | |
| 290 | | /*------------------------------------------------- |
| 291 | | DEVICE_IMAGE_LOAD( cartslot ) |
| 292 | | -------------------------------------------------*/ |
| 293 | | |
| 294 | | static DEVICE_IMAGE_LOAD( cartslot ) |
| 295 | | { |
| 296 | | int result; |
| 297 | | device_t *device = &image.device(); |
| 298 | | cartslot_t *cart = get_token(device); |
| 299 | | const cartslot_config *config = get_config(device); |
| 300 | | |
| 301 | | /* if this cartridge has a custom DEVICE_IMAGE_LOAD, use it */ |
| 302 | | if (config->device_load != NULL) |
| 303 | | return (*config->device_load)(image); |
| 304 | | |
| 305 | | /* try opening this as if it were a multicart */ |
| 306 | | multicart_open(image.filename(), device->machine->gamedrv->name, MULTICART_FLAGS_LOAD_RESOURCES, &cart->mc); |
| 307 | | if (cart->mc == NULL) |
| 308 | | { |
| 309 | | |
| 310 | | |
| 311 | | /* otherwise try the normal route */ |
| 312 | | result = process_cartridge(&image, PROCESS_LOAD); |
| 313 | | if (result != IMAGE_INIT_PASS) |
| 314 | | return result; |
| 315 | | } |
| 316 | | |
| 317 | | return IMAGE_INIT_PASS; |
| 318 | | } |
| 319 | | |
| 320 | | |
| 321 | | /*------------------------------------------------- |
| 322 | | DEVICE_IMAGE_UNLOAD( cartslot ) |
| 323 | | -------------------------------------------------*/ |
| 324 | | |
| 325 | | static DEVICE_IMAGE_UNLOAD( cartslot ) |
| 326 | | { |
| 327 | | device_t *device = &image.device(); |
| 328 | | cartslot_t *cart = get_token(device); |
| 329 | | const cartslot_config *config = get_config(device); |
| 330 | | |
| 331 | | /* if this cartridge has a custom DEVICE_IMAGE_UNLOAD, use it */ |
| 332 | | if (config->device_unload != NULL) |
| 333 | | { |
| 334 | | (*config->device_unload)(image); |
| 335 | | return; |
| 336 | | } |
| 337 | | |
| 338 | | if (cart->mc != NULL) |
| 339 | | { |
| 340 | | multicart_close(cart->mc); |
| 341 | | cart->mc = NULL; |
| 342 | | } |
| 343 | | |
| 344 | | process_cartridge(&image, PROCESS_CLEAR); |
| 345 | | } |
| 346 | | |
| 347 | | |
| 348 | | /*------------------------------------------------- |
| 349 | | identify_pcb |
| 350 | | -------------------------------------------------*/ |
| 351 | | |
| 352 | | static const cartslot_pcb_type *identify_pcb(device_image_interface &image) |
| 353 | | { |
| 354 | | const cartslot_config *config = get_config(&image.device()); |
| 355 | | astring pcb_name; |
| 356 | | const cartslot_pcb_type *pcb_type = NULL; |
| 357 | | multicart_t *mc; |
| 358 | | int i; |
| 359 | | |
| 360 | | if (image.software_entry() == NULL && image.exists()) |
| 361 | | { |
| 362 | | /* try opening this as if it were a multicart */ |
| 363 | | multicart_open_error me = multicart_open(image.filename(), image.device().machine->gamedrv->name, MULTICART_FLAGS_DONT_LOAD_RESOURCES, &mc); |
| 364 | | if (me == MCERR_NONE) |
| 365 | | { |
| 366 | | /* this was a multicart - read from it */ |
| 367 | | astring_cpyc(&pcb_name, mc->pcb_type); |
| 368 | | multicart_close(mc); |
| 369 | | } |
| 370 | | else |
| 371 | | { |
| 372 | | if (me != MCERR_NOT_MULTICART) |
| 373 | | fatalerror("multicart error: %s", multicart_error_text(me)); |
| 374 | | |
| 375 | | /* Force fetching of image metadata */ |
| 376 | | image.crc(); |
| 377 | | |
| 378 | | if (image.pcb() != NULL) |
| 379 | | { |
| 380 | | /* read from hash file */ |
| 381 | | astring_cpyc(&pcb_name, image.pcb()); |
| 382 | | } |
| 383 | | } |
| 384 | | |
| 385 | | /* look for PCB type with matching name */ |
| 386 | | for (i = 0; (i < ARRAY_LENGTH(config->pcb_types)) && (config->pcb_types[i].name != NULL); i++) |
| 387 | | { |
| 388 | | if ((config->pcb_types[i].name[0] == '\0') || !strcmp(astring_c(&pcb_name), config->pcb_types[i].name)) |
| 389 | | { |
| 390 | | pcb_type = &config->pcb_types[i]; |
| 391 | | break; |
| 392 | | } |
| 393 | | } |
| 394 | | |
| 395 | | /* check for unknown PCB type */ |
| 396 | | if ((mc != NULL) && (pcb_type == NULL)) |
| 397 | | fatalerror("Unknown PCB type \"%s\"", astring_c(&pcb_name)); |
| 398 | | } |
| 399 | | else |
| 400 | | { |
| 401 | | /* no device loaded; use the default */ |
| 402 | | pcb_type = (config->pcb_types[0].name != NULL) ? &config->pcb_types[0] : NULL; |
| 403 | | } |
| 404 | | return pcb_type; |
| 405 | | } |
| 406 | | |
| 407 | | /*------------------------------------------------- |
| 408 | | DEVICE_IMAGE_GET_DEVICES(cartslot) |
| 409 | | -------------------------------------------------*/ |
| 410 | | static DEVICE_IMAGE_GET_DEVICES(cartslot) |
| 411 | | { |
| 412 | | const cartslot_pcb_type *pcb_type; |
| 413 | | device_t *device = &image.device(); |
| 414 | | |
| 415 | | pcb_type = identify_pcb(image); |
| 416 | | if (pcb_type != NULL) |
| 417 | | { |
| 418 | | image_add_device_with_subdevices(device,pcb_type->devtype,TAG_PCB,0); |
| 419 | | } |
| 420 | | } |
| 421 | | |
| 422 | | /*------------------------------------------------- |
| 423 | | DEVICE_IMAGE_SOFTLIST_LOAD(cartslot) |
| 424 | | -------------------------------------------------*/ |
| 425 | | static DEVICE_IMAGE_SOFTLIST_LOAD(cartslot) |
| 426 | | { |
| 427 | | load_software_part_region( &image.device(), swlist, swname, start_entry ); |
| 428 | | return TRUE; |
| 429 | | } |
| 430 | | |
| 431 | | /*------------------------------------------------- |
| 432 | | DEVICE_GET_INFO( cartslot ) |
| 433 | | -------------------------------------------------*/ |
| 434 | | |
| 435 | | DEVICE_GET_INFO( cartslot ) |
| 436 | | { |
| 437 | | switch(state) |
| 438 | | { |
| 439 | | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 440 | | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(cartslot_t); break; |
| 441 | | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(cartslot_config); break; |
| 442 | | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_CARTSLOT; break; |
| 443 | | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 444 | | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 0; break; |
| 445 | | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 0; break; |
| 446 | | case DEVINFO_INT_IMAGE_RESET_ON_LOAD: info->i = 1; break; |
| 447 | | case DEVINFO_INT_IMAGE_MUST_BE_LOADED: |
| 448 | | if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config()) { |
| 449 | | info->i = get_config_dev(device)->must_be_loaded; |
| 450 | | } else { |
| 451 | | info->i = 0; |
| 452 | | } |
| 453 | | break; |
| 454 | | |
| 455 | | /* --- the following bits of info are returned as pointers to functions --- */ |
| 456 | | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(cartslot); break; |
| 457 | | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(cartslot); break; |
| 458 | | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(cartslot); break; |
| 459 | | case DEVINFO_FCT_IMAGE_GET_DEVICES: info->f = (genf *) DEVICE_IMAGE_GET_DEVICES_NAME(cartslot); break; |
| 460 | | case DEVINFO_FCT_IMAGE_SOFTLIST_LOAD: info->f = (genf *) DEVICE_IMAGE_SOFTLIST_LOAD_NAME(cartslot); break; |
| 461 | | case DEVINFO_FCT_IMAGE_PARTIAL_HASH: |
| 462 | | if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config() && get_config_dev(device)->device_partialhash) { |
| 463 | | info->f = (genf *) get_config_dev(device)->device_partialhash; |
| 464 | | } else { |
| 465 | | info->f = NULL; |
| 466 | | } |
| 467 | | break; |
| 468 | | |
| 469 | | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 470 | | case DEVINFO_STR_NAME: strcpy(info->s, "Cartslot"); break; |
| 471 | | case DEVINFO_STR_FAMILY: strcpy(info->s, "Cartslot"); break; |
| 472 | | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 473 | | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: |
| 474 | | if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config() && get_config_dev(device)->extensions ) |
| 475 | | { |
| 476 | | strcpy(info->s, get_config_dev(device)->extensions); |
| 477 | | } |
| 478 | | else |
| 479 | | { |
| 480 | | strcpy(info->s, "bin"); |
| 481 | | } |
| 482 | | break; |
| 483 | | case DEVINFO_STR_IMAGE_INTERFACE: |
| 484 | | if ( device && downcast<const legacy_image_device_config_base *>(device)->inline_config() && get_config_dev(device)->interface ) |
| 485 | | { |
| 486 | | strcpy(info->s, get_config_dev(device)->interface ); |
| 487 | | } |
| 488 | | break; |
| 489 | | } |
| 490 | | } |
| 491 | | |
| 492 | | DEFINE_LEGACY_IMAGE_DEVICE(CARTSLOT, cartslot); |
| 493 | | |
| 494 | | |
| 495 | | //************************************************************************** |
| 496 | | // DEVICE CONFIG CARTSLOT INTERFACE |
| 497 | | //************************************************************************** |
| 498 | | |
| 499 | | //------------------------------------------------- |
| 500 | | // device_config_cart_slot_interface - constructor |
| 501 | | //------------------------------------------------- |
| 502 | | |
| 503 | | device_config_cart_slot_interface::device_config_cart_slot_interface(const machine_config &mconfig, device_config &devconfig) |
| 504 | | : device_config_interface(mconfig, devconfig) |
| 505 | | { |
| 506 | | } |
| 507 | | |
| 508 | | |
| 509 | | //------------------------------------------------- |
| 510 | | // ~device_config_cart_slot_interface - destructor |
| 511 | | //------------------------------------------------- |
| 512 | | |
| 513 | | device_config_cart_slot_interface::~device_config_cart_slot_interface() |
| 514 | | { |
| 515 | | } |
| 516 | | |
| 517 | | |
| 518 | | |
| 519 | | //************************************************************************** |
| 520 | | // DEVICE CARTSLOT INTERFACE |
| 521 | | //************************************************************************** |
| 522 | | |
| 523 | | //------------------------------------------------- |
| 524 | | // device_cart_slot_interface - constructor |
| 525 | | //------------------------------------------------- |
| 526 | | |
| 527 | | device_cart_slot_interface::device_cart_slot_interface(running_machine &machine, const device_config &config, device_t &device) |
| 528 | | : device_interface(machine, config, device), |
| 529 | | m_cart_slot_config(dynamic_cast<const device_config_cart_slot_interface &>(config)) |
| 530 | | { |
| 531 | | } |
| 532 | | |
| 533 | | |
| 534 | | //------------------------------------------------- |
| 535 | | // ~device_cart_slot_interface - destructor |
| 536 | | //------------------------------------------------- |
| 537 | | |
| 538 | | device_cart_slot_interface::~device_cart_slot_interface() |
| 539 | | { |
| 540 | | } |
| 541 | | |
| 542 | | //************************************************************************** |
| 543 | | // LEGACY cart_slot DEVICE CONFIGURATION |
| 544 | | //************************************************************************** |
| 545 | | |
| 546 | | //------------------------------------------------- |
| 547 | | // legacy_cart_slot_device_config_base - constructor |
| 548 | | //------------------------------------------------- |
| 549 | | |
| 550 | | legacy_cart_slot_device_config_base::legacy_cart_slot_device_config_base(const machine_config &mconfig, device_type type, const char *tag, const device_config *owner, UINT32 clock, device_get_config_func get_config) |
| 551 | | : legacy_device_config_base(mconfig, type, tag, owner, clock, get_config), |
| 552 | | device_config_cart_slot_interface(mconfig, *this) |
| 553 | | { |
| 554 | | } |
| 555 | | |
| 556 | | |
| 557 | | //************************************************************************** |
| 558 | | // LIVE LEGACY cart_slot DEVICE |
| 559 | | //************************************************************************** |
| 560 | | |
| 561 | | //------------------------------------------------- |
| 562 | | // legacy_cart_slot_device_base - constructor |
| 563 | | //------------------------------------------------- |
| 564 | | |
| 565 | | legacy_cart_slot_device_base::legacy_cart_slot_device_base(running_machine &machine, const device_config &config) |
| 566 | | : legacy_device_base(machine, config), |
| 567 | | device_cart_slot_interface(machine, config, *this) |
| 568 | | { |
| 569 | | } |
src/mess/devices/cartslot.h
| r9927 | r9928 | |
| 1 | | /*************************************************************************** |
| 2 | | |
| 3 | | Cartrige loading |
| 4 | | |
| 5 | | ***************************************************************************/ |
| 6 | | |
| 7 | | #ifndef __CARTSLOT_H__ |
| 8 | | #define __CARTSLOT_H__ |
| 9 | | |
| 10 | | #include "image.h" |
| 11 | | #include "multcart.h" |
| 12 | | |
| 13 | | |
| 14 | | /*************************************************************************** |
| 15 | | MACROS |
| 16 | | ***************************************************************************/ |
| 17 | | #define TAG_PCB "pcb" |
| 18 | | |
| 19 | | #define ROM_CART_LOAD(tag,offset,length,flags) \ |
| 20 | | { NULL, tag, offset, length, ROMENTRYTYPE_CARTRIDGE | (flags) }, |
| 21 | | |
| 22 | | #define ROM_MIRROR 0x01000000 |
| 23 | | #define ROM_NOMIRROR 0x00000000 |
| 24 | | #define ROM_FULLSIZE 0x02000000 |
| 25 | | #define ROM_FILL_FF 0x04000000 |
| 26 | | #define ROM_NOCLEAR 0x08000000 |
| 27 | | |
| 28 | | DECLARE_LEGACY_IMAGE_DEVICE(CARTSLOT, cartslot); |
| 29 | | |
| 30 | | |
| 31 | | |
| 32 | | /*************************************************************************** |
| 33 | | TYPE DEFINITIONS |
| 34 | | ***************************************************************************/ |
| 35 | | |
| 36 | | typedef struct _cartslot_t cartslot_t; |
| 37 | | struct _cartslot_t |
| 38 | | { |
| 39 | | device_t *pcb_device; |
| 40 | | multicart_t *mc; |
| 41 | | }; |
| 42 | | |
| 43 | | |
| 44 | | typedef struct _cartslot_pcb_type cartslot_pcb_type; |
| 45 | | struct _cartslot_pcb_type |
| 46 | | { |
| 47 | | const char * name; |
| 48 | | device_type devtype; |
| 49 | | }; |
| 50 | | |
| 51 | | |
| 52 | | typedef struct _cartslot_config cartslot_config; |
| 53 | | struct _cartslot_config |
| 54 | | { |
| 55 | | const char * extensions; |
| 56 | | const char * interface; |
| 57 | | int must_be_loaded; |
| 58 | | device_start_func device_start; |
| 59 | | device_image_load_func device_load; |
| 60 | | device_image_unload_func device_unload; |
| 61 | | device_image_partialhash_func device_partialhash; |
| 62 | | cartslot_pcb_type pcb_types[16]; |
| 63 | | }; |
| 64 | | |
| 65 | | |
| 66 | | /*************************************************************************** |
| 67 | | FUNCTION PROTOTYPES |
| 68 | | ***************************************************************************/ |
| 69 | | |
| 70 | | /* accesses the PCB associated with this cartslot */ |
| 71 | | device_t *cartslot_get_pcb(device_t *device); |
| 72 | | |
| 73 | | /* accesses a particular socket */ |
| 74 | | void *cartslot_get_socket(device_t *device, const char *socket_name); |
| 75 | | |
| 76 | | /* accesses a particular socket; gets the length of the associated resource */ |
| 77 | | int cartslot_get_resource_length(device_t *device, const char *socket_name); |
| 78 | | |
| 79 | | |
| 80 | | /*************************************************************************** |
| 81 | | DEVICE CONFIGURATION MACROS |
| 82 | | ***************************************************************************/ |
| 83 | | |
| 84 | | #define MCFG_CARTSLOT_ADD(_tag) \ |
| 85 | | MCFG_DEVICE_ADD(_tag, CARTSLOT, 0) \ |
| 86 | | |
| 87 | | #define MCFG_CARTSLOT_MODIFY(_tag) \ |
| 88 | | MCFG_DEVICE_MODIFY(_tag) \ |
| 89 | | |
| 90 | | #define MCFG_CARTSLOT_EXTENSION_LIST(_extensions) \ |
| 91 | | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, extensions, _extensions) |
| 92 | | |
| 93 | | #define MCFG_CARTSLOT_NOT_MANDATORY \ |
| 94 | | MCFG_DEVICE_CONFIG_DATA32(cartslot_config, must_be_loaded, FALSE) |
| 95 | | |
| 96 | | #define MCFG_CARTSLOT_MANDATORY \ |
| 97 | | MCFG_DEVICE_CONFIG_DATA32(cartslot_config, must_be_loaded, TRUE) |
| 98 | | |
| 99 | | #define MCFG_CARTSLOT_START(_start) \ |
| 100 | | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_start, DEVICE_START_NAME(_start)) |
| 101 | | |
| 102 | | #define MCFG_CARTSLOT_LOAD(_load) \ |
| 103 | | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_load, DEVICE_IMAGE_LOAD_NAME(_load)) |
| 104 | | |
| 105 | | #define MCFG_CARTSLOT_UNLOAD(_unload) \ |
| 106 | | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_unload, DEVICE_IMAGE_UNLOAD_NAME(_unload)) |
| 107 | | |
| 108 | | #define MCFG_CARTSLOT_PARTIALHASH(_partialhash) \ |
| 109 | | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, device_partialhash, _partialhash) |
| 110 | | |
| 111 | | #define MCFG_CARTSLOT_PCBTYPE(_index, _pcb_type_name, _pcb_devtype) \ |
| 112 | | MCFG_DEVICE_CONFIG_DATAPTR_ARRAY_MEMBER(cartslot_config, pcb_types, _index, cartslot_pcb_type, name, _pcb_type_name) \ |
| 113 | | MCFG_DEVICE_CONFIG_DATAPTR_ARRAY_MEMBER(cartslot_config, pcb_types, _index, cartslot_pcb_type, devtype, _pcb_devtype) |
| 114 | | |
| 115 | | #define MCFG_CARTSLOT_INTERFACE(_interface) \ |
| 116 | | MCFG_DEVICE_CONFIG_DATAPTR(cartslot_config, interface, _interface ) |
| 117 | | |
| 118 | | #define DECLARE_LEGACY_CART_SLOT_DEVICE(name, basename) _DECLARE_LEGACY_DEVICE(name, basename, basename##_device_config, basename##_device, legacy_cart_slot_device_config_base, legacy_cart_slot_device_base) |
| 119 | | #define DEFINE_LEGACY_CART_SLOT_DEVICE(name, basename) _DEFINE_LEGACY_DEVICE(name, basename, basename##_device_config, basename##_device, legacy_cart_slot_device_config_base, legacy_cart_slot_device_base) |
| 120 | | |
| 121 | | // ======================> device_config_cart_slot_interface |
| 122 | | |
| 123 | | // class representing interface-specific configuration cart_slot |
| 124 | | class device_config_cart_slot_interface : public device_config_interface |
| 125 | | { |
| 126 | | public: |
| 127 | | // construction/destruction |
| 128 | | device_config_cart_slot_interface(const machine_config &mconfig, device_config &device); |
| 129 | | virtual ~device_config_cart_slot_interface(); |
| 130 | | }; |
| 131 | | |
| 132 | | |
| 133 | | |
| 134 | | // ======================> device_cart_slot_interface |
| 135 | | |
| 136 | | // class representing interface-specific live cart_slot |
| 137 | | class device_cart_slot_interface : public device_interface |
| 138 | | { |
| 139 | | public: |
| 140 | | // construction/destruction |
| 141 | | device_cart_slot_interface(running_machine &machine, const device_config &config, device_t &device); |
| 142 | | virtual ~device_cart_slot_interface(); |
| 143 | | |
| 144 | | // configuration access |
| 145 | | const device_config_cart_slot_interface &cart_slot_config() const { return m_cart_slot_config; } |
| 146 | | protected: |
| 147 | | |
| 148 | | // configuration |
| 149 | | const device_config_cart_slot_interface &m_cart_slot_config; // reference to our device_config_execute_interface |
| 150 | | }; |
| 151 | | // ======================> legacy_cart_slot_device_config |
| 152 | | |
| 153 | | // legacy_cart_slot_device_config is a device_config with a cart_slot interface |
| 154 | | class legacy_cart_slot_device_config_base : public legacy_device_config_base, |
| 155 | | public device_config_cart_slot_interface |
| 156 | | { |
| 157 | | protected: |
| 158 | | // construction/destruction |
| 159 | | legacy_cart_slot_device_config_base(const machine_config &mconfig, device_type type, const char *tag, const device_config *owner, UINT32 clock, device_get_config_func get_config); |
| 160 | | public: |
| 161 | | INT64 get_config_int(UINT32 state) const { return get_legacy_config_int(state); } |
| 162 | | genf *get_config_fct(UINT32 state) const { return get_legacy_config_fct(state); } |
| 163 | | void *get_config_ptr(UINT32 state) const { return get_legacy_config_ptr(state); } |
| 164 | | }; |
| 165 | | |
| 166 | | |
| 167 | | |
| 168 | | // ======================> legacy_cart_slot_device |
| 169 | | |
| 170 | | // legacy_cart_slot_device is a legacy_device_base with a cart_slot interface |
| 171 | | class legacy_cart_slot_device_base : public legacy_device_base, |
| 172 | | public device_cart_slot_interface |
| 173 | | { |
| 174 | | protected: |
| 175 | | // construction/destruction |
| 176 | | legacy_cart_slot_device_base(running_machine &machine, const device_config &config); |
| 177 | | |
| 178 | | // device_cart_slot_interface overrides |
| 179 | | }; |
| 180 | | #endif /* __cart_slot_H__ */ |
src/mess/devices/bitbngr.c
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | bitbngr.c |
| 4 | | |
| 5 | | TRS style "bitbanger" serial port |
| 6 | | |
| 7 | | *********************************************************************/ |
| 8 | | |
| 9 | | #include "emu.h" |
| 10 | | #include "bitbngr.h" |
| 11 | | |
| 12 | | |
| 13 | | |
| 14 | | /*************************************************************************** |
| 15 | | TYPE DEFINITIONS |
| 16 | | ***************************************************************************/ |
| 17 | | |
| 18 | | typedef struct _bitbanger_token bitbanger_token; |
| 19 | | struct _bitbanger_token |
| 20 | | { |
| 21 | | emu_timer *bitbanger_output_timer; |
| 22 | | emu_timer *bitbanger_input_timer; |
| 23 | | int output_value; |
| 24 | | int build_count; |
| 25 | | int build_byte; |
| 26 | | attotime idle_delay; |
| 27 | | attotime current_baud; |
| 28 | | UINT32 input_buffer_size; |
| 29 | | UINT32 input_buffer_cursor; |
| 30 | | int mode; |
| 31 | | int baud; |
| 32 | | int tune; |
| 33 | | UINT8 input_buffer[1000]; |
| 34 | | }; |
| 35 | | |
| 36 | | |
| 37 | | |
| 38 | | /*************************************************************************** |
| 39 | | FUNCTION PROTOTYPES |
| 40 | | ***************************************************************************/ |
| 41 | | |
| 42 | | static TIMER_CALLBACK(bitbanger_output_timer); |
| 43 | | static TIMER_CALLBACK(bitbanger_input_timer); |
| 44 | | |
| 45 | | |
| 46 | | |
| 47 | | /*************************************************************************** |
| 48 | | INLINE FUNCTIONS |
| 49 | | ***************************************************************************/ |
| 50 | | |
| 51 | | |
| 52 | | /*------------------------------------------------- |
| 53 | | get_token - safely gets the bitbanger data |
| 54 | | -------------------------------------------------*/ |
| 55 | | |
| 56 | | INLINE bitbanger_token *get_token(device_t *device) |
| 57 | | { |
| 58 | | assert(device != NULL); |
| 59 | | assert(device->type() == BITBANGER); |
| 60 | | return (bitbanger_token *) downcast<legacy_device_base *>(device)->token(); |
| 61 | | } |
| 62 | | |
| 63 | | |
| 64 | | /*------------------------------------------------- |
| 65 | | get_config - safely gets the bitbanger config |
| 66 | | -------------------------------------------------*/ |
| 67 | | |
| 68 | | INLINE const bitbanger_config *get_config(device_t *device) |
| 69 | | { |
| 70 | | assert(device != NULL); |
| 71 | | assert(device->type() == BITBANGER); |
| 72 | | return (const bitbanger_config *) device->baseconfig().static_config(); |
| 73 | | } |
| 74 | | |
| 75 | | |
| 76 | | |
| 77 | | /*************************************************************************** |
| 78 | | IMPLEMENTATION |
| 79 | | ***************************************************************************/ |
| 80 | | |
| 81 | | |
| 82 | | /*------------------------------------------------- |
| 83 | | native_output - outputs data to a file |
| 84 | | -------------------------------------------------*/ |
| 85 | | |
| 86 | | static void native_output(device_t *bitbanger, UINT8 data) |
| 87 | | { |
| 88 | | device_image_interface *image = dynamic_cast<device_image_interface *>(bitbanger); |
| 89 | | if (image->exists()) |
| 90 | | { |
| 91 | | image->fwrite(&data, 1); |
| 92 | | } |
| 93 | | } |
| 94 | | |
| 95 | | |
| 96 | | /*------------------------------------------------- |
| 97 | | native_input - inputs data from a file |
| 98 | | -------------------------------------------------*/ |
| 99 | | |
| 100 | | static UINT32 native_input(device_t *bitbanger, void *buffer, UINT32 length) |
| 101 | | { |
| 102 | | device_image_interface *image = dynamic_cast<device_image_interface *>(bitbanger); |
| 103 | | if (image->exists()) |
| 104 | | { |
| 105 | | return image->fread(buffer, length); |
| 106 | | } |
| 107 | | |
| 108 | | return 0; |
| 109 | | } |
| 110 | | |
| 111 | | |
| 112 | | /*------------------------------------------------- |
| 113 | | bitbanger_mode_string |
| 114 | | -------------------------------------------------*/ |
| 115 | | const char *bitbanger_mode_string(device_t *device) |
| 116 | | { |
| 117 | | bitbanger_token *bi = get_token(device); |
| 118 | | static const char *const modes[] = {"Printer", "Modem"}; |
| 119 | | |
| 120 | | return(modes[bi->mode]); |
| 121 | | } |
| 122 | | |
| 123 | | |
| 124 | | /*------------------------------------------------- |
| 125 | | bitbanger_inc_mode |
| 126 | | -------------------------------------------------*/ |
| 127 | | bool bitbanger_inc_mode(device_t *device, bool test) |
| 128 | | { |
| 129 | | bitbanger_token *bi = get_token(device); |
| 130 | | int adjust_mode = (int)bi->mode + 1; |
| 131 | | |
| 132 | | if( adjust_mode >= BITBANGER_MODE_MAX ) |
| 133 | | return FALSE; |
| 134 | | |
| 135 | | if( !test) |
| 136 | | bi->mode = adjust_mode; |
| 137 | | |
| 138 | | return TRUE; |
| 139 | | } |
| 140 | | |
| 141 | | |
| 142 | | /*------------------------------------------------- |
| 143 | | bitbanger_dec_mode |
| 144 | | -------------------------------------------------*/ |
| 145 | | bool bitbanger_dec_mode(device_t *device, bool test) |
| 146 | | { |
| 147 | | bitbanger_token *bi = get_token(device); |
| 148 | | int adjust_mode = bi->mode - 1; |
| 149 | | |
| 150 | | if( adjust_mode < 0 ) |
| 151 | | return FALSE; |
| 152 | | |
| 153 | | if( !test) |
| 154 | | bi->mode = adjust_mode; |
| 155 | | |
| 156 | | return TRUE; |
| 157 | | } |
| 158 | | |
| 159 | | |
| 160 | | /*------------------------------------------------- |
| 161 | | bitbanger_tune_string |
| 162 | | -------------------------------------------------*/ |
| 163 | | const char *bitbanger_tune_string(device_t *device) |
| 164 | | { |
| 165 | | bitbanger_token *bi = get_token(device); |
| 166 | | static const char *const tunes[] = {"-2.0%", "-1.75%", "-1.5%", "-1.25%", "-1.0%", "-0.75%", "-0.5", "-0.25%", "\xc2\xb1""0%", |
| 167 | | "+0.25%", "+0.5%", "+0.75%", "+1.0%", "+1.25%", "+1.5%", "+1.75%", "+2.0%"}; |
| 168 | | |
| 169 | | return(tunes[bi->tune]); |
| 170 | | } |
| 171 | | |
| 172 | | |
| 173 | | /*------------------------------------------------- |
| 174 | | bitbanger_tune_value |
| 175 | | -------------------------------------------------*/ |
| 176 | | static float bitbanger_tune_value(device_t *device) |
| 177 | | { |
| 178 | | bitbanger_token *bi = get_token(device); |
| 179 | | static const float tunes[] = {0.97f, 0.9825f, 0.985f, 0.9875f, 0.99f, 0.9925f, 0.995f, 0.9975f, 1.0f, |
| 180 | | 1.0025f, 1.005f, 1.0075f, 1.01f, 1.0125f, 1.015f, 1.0175f, 1.02f}; |
| 181 | | |
| 182 | | return(tunes[bi->tune]); |
| 183 | | } |
| 184 | | |
| 185 | | |
| 186 | | /*------------------------------------------------- |
| 187 | | bitbanger_baud_value |
| 188 | | -------------------------------------------------*/ |
| 189 | | |
| 190 | | static UINT32 bitbanger_baud_value(device_t *device) |
| 191 | | { |
| 192 | | bitbanger_token *bi = get_token(device); |
| 193 | | static const float bauds[] = { 150.0f, 300.0f, 600.0f, 1200.0f, 2400.0f, 4800.0f, 9600.0f, |
| 194 | | 14400.0f, 28800.0f, 38400.0f, 57600.0f, 115200.0f}; |
| 195 | | float result = bitbanger_tune_value(device) * bauds[bi->baud]; |
| 196 | | return (UINT32)result; |
| 197 | | } |
| 198 | | |
| 199 | | |
| 200 | | /*------------------------------------------------- |
| 201 | | bitbanger_baud_string |
| 202 | | -------------------------------------------------*/ |
| 203 | | |
| 204 | | const char *bitbanger_baud_string(device_t *device) |
| 205 | | { |
| 206 | | bitbanger_token *bi = get_token(device); |
| 207 | | static const char *const bauds[] = { "150", "300", "600", "1200", "2400", "4800", |
| 208 | | "9600", "14400", "28800", "38400", "57600", "115200"}; |
| 209 | | |
| 210 | | return(bauds[bi->baud]); |
| 211 | | } |
| 212 | | |
| 213 | | |
| 214 | | /*------------------------------------------------- |
| 215 | | bitbanger_inc_baud |
| 216 | | -------------------------------------------------*/ |
| 217 | | bool bitbanger_inc_baud(device_t *device, bool test) |
| 218 | | { |
| 219 | | bitbanger_token *bi = get_token(device); |
| 220 | | int adjust_baud = (int)bi->baud + 1; |
| 221 | | |
| 222 | | if( adjust_baud >= BITBANGER_BAUD_MAX ) |
| 223 | | return FALSE; |
| 224 | | |
| 225 | | if( !test) |
| 226 | | { |
| 227 | | bi->baud = adjust_baud; |
| 228 | | bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device)); |
| 229 | | } |
| 230 | | |
| 231 | | return TRUE; |
| 232 | | } |
| 233 | | |
| 234 | | |
| 235 | | /*------------------------------------------------- |
| 236 | | bitbanger_dec_baud |
| 237 | | -------------------------------------------------*/ |
| 238 | | bool bitbanger_dec_baud(device_t *device, bool test) |
| 239 | | { |
| 240 | | bitbanger_token *bi = get_token(device); |
| 241 | | int adjust_baud = bi->baud - 1; |
| 242 | | |
| 243 | | if( adjust_baud < 0 ) |
| 244 | | return FALSE; |
| 245 | | |
| 246 | | if( !test) |
| 247 | | { |
| 248 | | bi->baud = adjust_baud; |
| 249 | | bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device)); |
| 250 | | } |
| 251 | | |
| 252 | | return TRUE; |
| 253 | | } |
| 254 | | |
| 255 | | |
| 256 | | /*------------------------------------------------- |
| 257 | | bitbanger_inc_tune |
| 258 | | -------------------------------------------------*/ |
| 259 | | bool bitbanger_inc_tune(device_t *device, bool test) |
| 260 | | { |
| 261 | | bitbanger_token *bi = get_token(device); |
| 262 | | int adjust_tune = (int)bi->tune + 1; |
| 263 | | |
| 264 | | if( adjust_tune >= BITBANGER_TUNE_MAX ) |
| 265 | | return FALSE; |
| 266 | | |
| 267 | | if( !test) |
| 268 | | { |
| 269 | | bi->tune = adjust_tune; |
| 270 | | bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device)); |
| 271 | | } |
| 272 | | |
| 273 | | return TRUE; |
| 274 | | } |
| 275 | | |
| 276 | | |
| 277 | | /*------------------------------------------------- |
| 278 | | bitbanger_dec_tune |
| 279 | | -------------------------------------------------*/ |
| 280 | | bool bitbanger_dec_tune(device_t *device, bool test) |
| 281 | | { |
| 282 | | bitbanger_token *bi = get_token(device); |
| 283 | | int adjust_tune = bi->tune - 1; |
| 284 | | |
| 285 | | if( adjust_tune < 0 ) |
| 286 | | return FALSE; |
| 287 | | |
| 288 | | if( !test) |
| 289 | | { |
| 290 | | bi->tune = adjust_tune; |
| 291 | | bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device)); |
| 292 | | } |
| 293 | | |
| 294 | | return TRUE; |
| 295 | | } |
| 296 | | |
| 297 | | |
| 298 | | /*------------------------------------------------- |
| 299 | | bitbanger_bytes_to_bits_81N |
| 300 | | -------------------------------------------------*/ |
| 301 | | static void bitbanger_bytes_to_bits_81N(device_t *img) |
| 302 | | { |
| 303 | | bitbanger_token *bi = get_token(img); |
| 304 | | UINT8 byte_buffer[100]; |
| 305 | | UINT32 byte_buffer_size, bit_buffer_size; |
| 306 | | int i, j; |
| 307 | | static const UINT8 bitmask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; |
| 308 | | |
| 309 | | bit_buffer_size = 0; |
| 310 | | byte_buffer_size = native_input(img, byte_buffer, sizeof(byte_buffer)); |
| 311 | | |
| 312 | | /* Translate byte buffer into bit buffer using: 1 start bit, 8 data bits, 1 stop bit, no parity */ |
| 313 | | |
| 314 | | for( i=0; i<byte_buffer_size; i++ ) |
| 315 | | { |
| 316 | | bi->input_buffer[bit_buffer_size++] = 0; |
| 317 | | |
| 318 | | for( j=0; j<8; j++ ) |
| 319 | | { |
| 320 | | if( byte_buffer[i] & bitmask[j] ) |
| 321 | | bi->input_buffer[bit_buffer_size++] = 1; |
| 322 | | else |
| 323 | | bi->input_buffer[bit_buffer_size++] = 0; |
| 324 | | } |
| 325 | | |
| 326 | | bi->input_buffer[bit_buffer_size++] = 1; |
| 327 | | } |
| 328 | | |
| 329 | | bi->input_buffer_size = bit_buffer_size; |
| 330 | | bi->input_buffer_cursor = 0; |
| 331 | | } |
| 332 | | |
| 333 | | |
| 334 | | /*------------------------------------------------- |
| 335 | | DEVICE_START(bitbanger) |
| 336 | | -------------------------------------------------*/ |
| 337 | | |
| 338 | | static DEVICE_START(bitbanger) |
| 339 | | { |
| 340 | | bitbanger_token *bi; |
| 341 | | const bitbanger_config *config = get_config(device); |
| 342 | | |
| 343 | | bi = get_token(device); |
| 344 | | |
| 345 | | /* output config */ |
| 346 | | bi->build_count = 0; |
| 347 | | bi->bitbanger_output_timer = timer_alloc(device->machine, bitbanger_output_timer, (void *) device); |
| 348 | | |
| 349 | | /* input config */ |
| 350 | | bi->bitbanger_input_timer = timer_alloc(device->machine, bitbanger_input_timer, (void *) device ); |
| 351 | | bi->idle_delay = ATTOTIME_IN_SEC(1); |
| 352 | | bi->input_buffer_size = 0; |
| 353 | | bi->input_buffer_cursor = 0; |
| 354 | | |
| 355 | | bi->mode = config->default_mode; |
| 356 | | bi->baud = config->default_baud; |
| 357 | | bi->tune = config->default_tune; |
| 358 | | bi->current_baud = ATTOTIME_IN_HZ(bitbanger_baud_value(device)); |
| 359 | | |
| 360 | | /* test callback */ |
| 361 | | if(!config->input_callback) |
| 362 | | fatalerror("Misconfigured bitbanger device: input_callback cannot be NULL\n"); |
| 363 | | } |
| 364 | | |
| 365 | | |
| 366 | | /*------------------------------------------------- |
| 367 | | TIMER_CALLBACK(bitbanger_output_timer) |
| 368 | | -------------------------------------------------*/ |
| 369 | | |
| 370 | | static TIMER_CALLBACK(bitbanger_output_timer) |
| 371 | | { |
| 372 | | device_t *device = (device_t *) ptr; |
| 373 | | bitbanger_token *bi = get_token(device); |
| 374 | | |
| 375 | | /* this ia harded coded for 8-1-N */ |
| 376 | | if( bi->output_value ) |
| 377 | | bi->build_byte |= 0x200; |
| 378 | | |
| 379 | | bi->build_byte >>= 1; |
| 380 | | bi->build_count--; |
| 381 | | |
| 382 | | if(bi->build_count == 0) |
| 383 | | { |
| 384 | | if( bi->output_value == 1 ) |
| 385 | | native_output( device, bi->build_byte ); |
| 386 | | else |
| 387 | | logerror("Bitbanger: Output framing error.\n" ); |
| 388 | | |
| 389 | | timer_reset(bi->bitbanger_output_timer, attotime_never); |
| 390 | | } |
| 391 | | } |
| 392 | | |
| 393 | | |
| 394 | | /*------------------------------------------------- |
| 395 | | TIMER_CALLBACK(bitbanger_input_timer) |
| 396 | | -------------------------------------------------*/ |
| 397 | | |
| 398 | | static TIMER_CALLBACK(bitbanger_input_timer) |
| 399 | | { |
| 400 | | device_t *device = (device_t *) ptr; |
| 401 | | bitbanger_token *bi = get_token(device); |
| 402 | | const bitbanger_config *config = get_config(device); |
| 403 | | |
| 404 | | if(bi->input_buffer_cursor == bi->input_buffer_size) |
| 405 | | { |
| 406 | | /* get more data */ |
| 407 | | bitbanger_bytes_to_bits_81N(device); |
| 408 | | |
| 409 | | if(bi->input_buffer_size == 0) |
| 410 | | { |
| 411 | | /* no more data, wait and try again */ |
| 412 | | bi->idle_delay = attotime_min(attotime_add(bi->idle_delay, ATTOTIME_IN_MSEC(100)), ATTOTIME_IN_SEC(1)); |
| 413 | | timer_adjust_oneshot(bi->bitbanger_input_timer, bi->idle_delay, 0); |
| 414 | | |
| 415 | | |
| 416 | | if( bi->mode == BITBANGER_MODEM ) |
| 417 | | config->input_callback(machine, 1); |
| 418 | | else |
| 419 | | config->input_callback(machine, 0); |
| 420 | | |
| 421 | | return; |
| 422 | | } |
| 423 | | else |
| 424 | | { |
| 425 | | bi->idle_delay = bi->current_baud; |
| 426 | | timer_adjust_periodic(bi->bitbanger_input_timer, bi->idle_delay, 0, bi->idle_delay); |
| 427 | | } |
| 428 | | } |
| 429 | | |
| 430 | | /* send bit to driver */ |
| 431 | | config->input_callback(machine, bi->input_buffer[(bi->input_buffer_cursor)++]); |
| 432 | | } |
| 433 | | |
| 434 | | |
| 435 | | /*------------------------------------------------- |
| 436 | | bitbanger_output - outputs data to a bitbanger |
| 437 | | port |
| 438 | | -------------------------------------------------*/ |
| 439 | | |
| 440 | | void bitbanger_output(device_t *device, int value) |
| 441 | | { |
| 442 | | bitbanger_token *bi = get_token(device); |
| 443 | | attotime one_point_five_baud; |
| 444 | | |
| 445 | | if( bi->build_count == 0 && bi->output_value == 1 && value == 0 ) |
| 446 | | { |
| 447 | | /* we found our start bit */ |
| 448 | | /* eight bits of data, plus one of stop */ |
| 449 | | bi->build_count = 9; |
| 450 | | bi->build_byte = 0; |
| 451 | | |
| 452 | | one_point_five_baud = attotime_add(bi->current_baud, attotime_div(bi->current_baud,2)); |
| 453 | | timer_adjust_periodic(bi->bitbanger_output_timer, one_point_five_baud, 0, bi->current_baud); |
| 454 | | } |
| 455 | | |
| 456 | | //fprintf(stderr,"%s, %d\n", attotime_string(timer_get_time(device->machine),9), value); |
| 457 | | bi->output_value = value; |
| 458 | | } |
| 459 | | |
| 460 | | |
| 461 | | /*------------------------------------------------- |
| 462 | | DEVICE_IMAGE_LOAD( bitbanger ) |
| 463 | | -------------------------------------------------*/ |
| 464 | | |
| 465 | | static DEVICE_IMAGE_LOAD( bitbanger ) |
| 466 | | { |
| 467 | | device_t *device = &image.device(); |
| 468 | | bitbanger_token *bi; |
| 469 | | bi = get_token(device); |
| 470 | | |
| 471 | | timer_enable(bi->bitbanger_input_timer, TRUE); |
| 472 | | timer_adjust_periodic(bi->bitbanger_input_timer, attotime_zero, 0, ATTOTIME_IN_SEC(1)); |
| 473 | | |
| 474 | | /* we don't need to do anything special */ |
| 475 | | return IMAGE_INIT_PASS; |
| 476 | | } |
| 477 | | |
| 478 | | |
| 479 | | /*------------------------------------------------- |
| 480 | | DEVICE_IMAGE_UNLOAD( bitbanger ) |
| 481 | | -------------------------------------------------*/ |
| 482 | | |
| 483 | | static DEVICE_IMAGE_UNLOAD( bitbanger ) |
| 484 | | { |
| 485 | | device_t *device = &image.device(); |
| 486 | | bitbanger_token *bi; |
| 487 | | bi = get_token(device); |
| 488 | | |
| 489 | | timer_enable(bi->bitbanger_input_timer, FALSE); |
| 490 | | } |
| 491 | | |
| 492 | | |
| 493 | | /*------------------------------------------------- |
| 494 | | DEVICE_GET_INFO(bitbanger) - device getinfo |
| 495 | | function |
| 496 | | -------------------------------------------------*/ |
| 497 | | |
| 498 | | DEVICE_GET_INFO(bitbanger) |
| 499 | | { |
| 500 | | switch(state) |
| 501 | | { |
| 502 | | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 503 | | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(bitbanger_token); break; |
| 504 | | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; |
| 505 | | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_PRINTER; break; |
| 506 | | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 507 | | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break; |
| 508 | | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break; |
| 509 | | |
| 510 | | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 511 | | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(bitbanger); break; |
| 512 | | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(bitbanger); break; |
| 513 | | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(bitbanger); break; |
| 514 | | |
| 515 | | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 516 | | case DEVINFO_STR_NAME: strcpy(info->s, "Bitbanger"); break; |
| 517 | | case DEVINFO_STR_FAMILY: strcpy(info->s, "Bitbanger"); break; |
| 518 | | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 519 | | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, "prn"); break; |
| 520 | | } |
| 521 | | } |
| 522 | | |
| 523 | | DEFINE_LEGACY_IMAGE_DEVICE(BITBANGER, bitbanger); |
src/mess/devices/multcart.c
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | multcart.c |
| 4 | | |
| 5 | | Multi-cartridge handling code |
| 6 | | |
| 7 | | *********************************************************************/ |
| 8 | | |
| 9 | | #include "multcart.h" |
| 10 | | #include "pool.h" |
| 11 | | #include "unzip.h" |
| 12 | | #include "corestr.h" |
| 13 | | #include "xmlfile.h" |
| 14 | | #include "emu.h" |
| 15 | | #include "hash.h" |
| 16 | | #include "machine/ram.h" |
| 17 | | |
| 18 | | /*************************************************************************** |
| 19 | | TYPE DEFINITIONS |
| 20 | | ***************************************************************************/ |
| 21 | | |
| 22 | | struct _multicart_private |
| 23 | | { |
| 24 | | object_pool * pool; |
| 25 | | zip_file * zip; |
| 26 | | }; |
| 27 | | |
| 28 | | |
| 29 | | typedef struct _multicart_load_state multicart_load_state; |
| 30 | | struct _multicart_load_state |
| 31 | | { |
| 32 | | multicart_t * multicart; |
| 33 | | zip_file * zip; |
| 34 | | xml_data_node * layout_xml; |
| 35 | | xml_data_node * resources_node; |
| 36 | | xml_data_node * pcb_node; |
| 37 | | multicart_resource * resources; |
| 38 | | multicart_socket * sockets; |
| 39 | | }; |
| 40 | | |
| 41 | | static const char error_text[14][30] = |
| 42 | | { |
| 43 | | "no error", |
| 44 | | "not a multicart", |
| 45 | | "module definition corrupt", |
| 46 | | "out of memory", |
| 47 | | "xml format error", |
| 48 | | "invalid file reference", |
| 49 | | "zip file error", |
| 50 | | "missing ram length", |
| 51 | | "invalid ram specification", |
| 52 | | "unknown resource type", |
| 53 | | "invalid resource reference", |
| 54 | | "invalid file format", |
| 55 | | "missing layout", |
| 56 | | "no pcb or resource found" |
| 57 | | }; |
| 58 | | |
| 59 | | const char *multicart_error_text(multicart_open_error error) |
| 60 | | { |
| 61 | | return error_text[(int)error]; |
| 62 | | } |
| 63 | | |
| 64 | | /*************************************************************************** |
| 65 | | IMPLEMENTATION |
| 66 | | ***************************************************************************/ |
| 67 | | |
| 68 | | /*------------------------------------------------- |
| 69 | | find_file |
| 70 | | -------------------------------------------------*/ |
| 71 | | |
| 72 | | static const zip_file_header *find_file(zip_file *zip, const char *filename) |
| 73 | | { |
| 74 | | const zip_file_header *header; |
| 75 | | for (header = zip_file_first_file(zip); header != NULL; header = zip_file_next_file(zip)) |
| 76 | | { |
| 77 | | if (!core_stricmp(header->filename, filename)) |
| 78 | | return header; |
| 79 | | } |
| 80 | | return NULL; |
| 81 | | } |
| 82 | | |
| 83 | | static const zip_file_header *find_file_crc(zip_file *zip, const char *filename, UINT32 crc) |
| 84 | | { |
| 85 | | const zip_file_header *header; |
| 86 | | for (header = zip_file_first_file(zip); header != NULL; header = zip_file_next_file(zip)) |
| 87 | | { |
| 88 | | // if the CRC and name both match, we're good |
| 89 | | // if the CRC matches and the name doesn't, we're still good |
| 90 | | if ((!core_stricmp(header->filename, filename)) && (header->crc == crc)) |
| 91 | | { |
| 92 | | return header; |
| 93 | | } |
| 94 | | else if (header->crc == crc) |
| 95 | | { |
| 96 | | return header; |
| 97 | | } |
| 98 | | } |
| 99 | | return NULL; |
| 100 | | } |
| 101 | | |
| 102 | | /*------------------------------------------------- |
| 103 | | find_pcb_and_resource_nodes |
| 104 | | -------------------------------------------------*/ |
| 105 | | |
| 106 | | static int find_pcb_and_resource_nodes(xml_data_node *layout_xml, |
| 107 | | xml_data_node **pcb_node, xml_data_node **resource_node) |
| 108 | | { |
| 109 | | xml_data_node *romset_node; |
| 110 | | xml_data_node *configuration_node; |
| 111 | | |
| 112 | | *pcb_node = NULL; |
| 113 | | *resource_node = NULL; |
| 114 | | |
| 115 | | romset_node = xml_get_sibling(layout_xml->child, "romset"); |
| 116 | | if (romset_node == NULL) |
| 117 | | return FALSE; |
| 118 | | |
| 119 | | configuration_node = xml_get_sibling(romset_node->child, "configuration"); |
| 120 | | if (configuration_node == NULL) |
| 121 | | return FALSE; |
| 122 | | |
| 123 | | *pcb_node = xml_get_sibling(configuration_node->child, "pcb"); |
| 124 | | if (*pcb_node == NULL) |
| 125 | | return FALSE; |
| 126 | | |
| 127 | | *resource_node = xml_get_sibling(romset_node->child, "resources"); |
| 128 | | if (*resource_node == NULL) |
| 129 | | return FALSE; |
| 130 | | |
| 131 | | return TRUE; |
| 132 | | } |
| 133 | | |
| 134 | | |
| 135 | | /*------------------------------------------------- |
| 136 | | get_resource_type |
| 137 | | -------------------------------------------------*/ |
| 138 | | |
| 139 | | static multicart_resource_type get_resource_type(const char *s) |
| 140 | | { |
| 141 | | multicart_resource_type result; |
| 142 | | if (!strcmp(s, "rom")) |
| 143 | | result = MULTICART_RESOURCE_TYPE_ROM; |
| 144 | | else if (!strcmp(s, "ram")) |
| 145 | | result = MULTICART_RESOURCE_TYPE_RAM; |
| 146 | | else |
| 147 | | result = MULTICART_RESOURCE_TYPE_INVALID; |
| 148 | | return result; |
| 149 | | } |
| 150 | | |
| 151 | | |
| 152 | | /*------------------------------------------------- |
| 153 | | load_rom_resource |
| 154 | | -------------------------------------------------*/ |
| 155 | | |
| 156 | | static multicart_open_error load_rom_resource(multicart_load_state *state, xml_data_node *resource_node, |
| 157 | | multicart_resource *resource) |
| 158 | | { |
| 159 | | const char *file, *crcstr, *sha1; |
| 160 | | const zip_file_header *header; |
| 161 | | zip_error ziperr; |
| 162 | | UINT32 crc; |
| 163 | | |
| 164 | | /* locate the 'file' attribute */ |
| 165 | | file = xml_get_attribute_string(resource_node, "file", NULL); |
| 166 | | if (file == NULL) |
| 167 | | return MCERR_XML_ERROR; |
| 168 | | |
| 169 | | if (!(crcstr = xml_get_attribute_string(resource_node, "crc", NULL))) |
| 170 | | { |
| 171 | | /* locate the file in the ZIP file */ |
| 172 | | header = find_file(state->zip, file); |
| 173 | | } |
| 174 | | else /* CRC tag is present, use it */ |
| 175 | | { |
| 176 | | crc = strtoul(crcstr, NULL, 16); |
| 177 | | header = find_file_crc(state->zip, file, crc); |
| 178 | | } |
| 179 | | if (header == NULL) |
| 180 | | return MCERR_INVALID_FILE_REF; |
| 181 | | resource->length = header->uncompressed_length; |
| 182 | | |
| 183 | | /* allocate bytes for this resource */ |
| 184 | | resource->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length); |
| 185 | | if (resource->ptr == NULL) |
| 186 | | return MCERR_OUT_OF_MEMORY; |
| 187 | | |
| 188 | | /* and decompress it */ |
| 189 | | ziperr = zip_file_decompress(state->zip, resource->ptr, resource->length); |
| 190 | | if (ziperr != ZIPERR_NONE) |
| 191 | | return MCERR_ZIP_ERROR; |
| 192 | | |
| 193 | | /* check SHA1 now */ |
| 194 | | if ((sha1 = xml_get_attribute_string(resource_node, "sha1", NULL))) |
| 195 | | { |
| 196 | | char calc_sha[256]; |
| 197 | | |
| 198 | | memset(calc_sha, 0, sizeof(calc_sha)); |
| 199 | | hash_compute(&calc_sha[0], (const unsigned char*)resource->ptr, resource->length, HASH_SHA1); |
| 200 | | |
| 201 | | if ((strncmp(sha1, &calc_sha[2], 20))) |
| 202 | | { |
| 203 | | return MCERR_INVALID_FILE_REF; |
| 204 | | } |
| 205 | | } |
| 206 | | |
| 207 | | return MCERR_NONE; |
| 208 | | } |
| 209 | | |
| 210 | | |
| 211 | | /*------------------------------------------------- |
| 212 | | load_ram_resource |
| 213 | | -------------------------------------------------*/ |
| 214 | | |
| 215 | | static multicart_open_error load_ram_resource(multicart_load_state *state, xml_data_node *resource_node, |
| 216 | | multicart_resource *resource) |
| 217 | | { |
| 218 | | const char *length_string; |
| 219 | | const char *ram_type; |
| 220 | | const char *ram_filename; |
| 221 | | |
| 222 | | astring *ram_pathname; |
| 223 | | |
| 224 | | /* locate the 'length' attribute */ |
| 225 | | length_string = xml_get_attribute_string(resource_node, "length", NULL); |
| 226 | | if (length_string == NULL) |
| 227 | | return MCERR_MISSING_RAM_LENGTH; |
| 228 | | |
| 229 | | /* ...and parse it */ |
| 230 | | resource->length = ram_parse_string(length_string); |
| 231 | | if (resource->length <= 0) |
| 232 | | return MCERR_INVALID_RAM_SPEC; |
| 233 | | |
| 234 | | /* allocate bytes for this resource */ |
| 235 | | resource->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length); |
| 236 | | if (resource->ptr == NULL) |
| 237 | | return MCERR_OUT_OF_MEMORY; |
| 238 | | |
| 239 | | /* Is this a persistent RAM resource? Then try to load it. */ |
| 240 | | ram_type = xml_get_attribute_string(resource_node, "type", NULL); |
| 241 | | if (ram_type != NULL) |
| 242 | | { |
| 243 | | if (strcmp(ram_type, "persistent")==0) |
| 244 | | { |
| 245 | | astring tmp; |
| 246 | | |
| 247 | | /* Get the file name. */ |
| 248 | | ram_filename = xml_get_attribute_string(resource_node, "file", NULL); |
| 249 | | if (ram_filename==NULL) |
| 250 | | return MCERR_XML_ERROR; |
| 251 | | |
| 252 | | ram_pathname = astring_assemble_3(&tmp, state->multicart->gamedrv_name, PATH_SEPARATOR, ram_filename); |
| 253 | | |
| 254 | | /* Save the file name so that we can write the contents on unloading. |
| 255 | | If the RAM resource has no filename, we know that it was volatile only. */ |
| 256 | | resource->filename = pool_strdup_lib(state->multicart->data->pool, astring_c(ram_pathname)); |
| 257 | | |
| 258 | | if (resource->filename == NULL) |
| 259 | | return MCERR_OUT_OF_MEMORY; |
| 260 | | |
| 261 | | image_battery_load_by_name(resource->filename, resource->ptr, resource->length, 0x00); |
| 262 | | } |
| 263 | | /* else this type is volatile, in which case we just have |
| 264 | | a memory expansion */ |
| 265 | | } |
| 266 | | return MCERR_NONE; |
| 267 | | } |
| 268 | | |
| 269 | | |
| 270 | | /*------------------------------------------------- |
| 271 | | load_resource |
| 272 | | -------------------------------------------------*/ |
| 273 | | |
| 274 | | static multicart_open_error load_resource(multicart_load_state *state, xml_data_node *resource_node, |
| 275 | | multicart_resource_type resource_type) |
| 276 | | { |
| 277 | | const char *id; |
| 278 | | multicart_open_error err; |
| 279 | | multicart_resource *resource; |
| 280 | | multicart_resource **next_resource; |
| 281 | | |
| 282 | | /* get the 'id' attribute; error if not present */ |
| 283 | | id = xml_get_attribute_string(resource_node, "id", NULL); |
| 284 | | if (id == NULL) |
| 285 | | return MCERR_XML_ERROR; |
| 286 | | |
| 287 | | /* allocate memory for the resource */ |
| 288 | | resource = (multicart_resource *)pool_malloc_lib(state->multicart->data->pool, sizeof(*resource)); |
| 289 | | if (resource == NULL) |
| 290 | | return MCERR_OUT_OF_MEMORY; |
| 291 | | memset(resource, 0, sizeof(*resource)); |
| 292 | | resource->type = resource_type; |
| 293 | | |
| 294 | | /* copy id */ |
| 295 | | resource->id = pool_strdup_lib(state->multicart->data->pool, id); |
| 296 | | if (resource->id == NULL) |
| 297 | | return MCERR_OUT_OF_MEMORY; |
| 298 | | |
| 299 | | switch(resource->type) |
| 300 | | { |
| 301 | | case MULTICART_RESOURCE_TYPE_ROM: |
| 302 | | err = load_rom_resource(state, resource_node, resource); |
| 303 | | if (err != MCERR_NONE) |
| 304 | | return err; |
| 305 | | break; |
| 306 | | |
| 307 | | case MULTICART_RESOURCE_TYPE_RAM: |
| 308 | | err = load_ram_resource(state, resource_node, resource); |
| 309 | | if (err != MCERR_NONE) |
| 310 | | return err; |
| 311 | | break; |
| 312 | | |
| 313 | | default: |
| 314 | | return MCERR_UNKNOWN_RESOURCE_TYPE; |
| 315 | | } |
| 316 | | |
| 317 | | /* append the resource */ |
| 318 | | for (next_resource = &state->resources; *next_resource; next_resource = &(*next_resource)->next) |
| 319 | | ; |
| 320 | | *next_resource = resource; |
| 321 | | |
| 322 | | return MCERR_NONE; |
| 323 | | } |
| 324 | | |
| 325 | | |
| 326 | | /*------------------------------------------------- |
| 327 | | load_all_resources |
| 328 | | -------------------------------------------------*/ |
| 329 | | |
| 330 | | static multicart_open_error load_all_resources(multicart_load_state *state) |
| 331 | | { |
| 332 | | multicart_open_error err; |
| 333 | | xml_data_node *resource_node; |
| 334 | | multicart_resource_type resource_type; |
| 335 | | |
| 336 | | for (resource_node = state->resources_node->child; resource_node != NULL; resource_node = resource_node->next) |
| 337 | | { |
| 338 | | resource_type = get_resource_type(resource_node->name); |
| 339 | | if (resource_type != MULTICART_RESOURCE_TYPE_INVALID) |
| 340 | | { |
| 341 | | err = load_resource(state, resource_node, resource_type); |
| 342 | | if (err != MCERR_NONE) |
| 343 | | return err; |
| 344 | | } |
| 345 | | } |
| 346 | | |
| 347 | | state->multicart->resources = state->resources; |
| 348 | | return MCERR_NONE; |
| 349 | | } |
| 350 | | |
| 351 | | /*------------------------------------------------- |
| 352 | | save_ram_resources. This is important for persistent RAM. All |
| 353 | | resources were allocated within the memory pool of this device and will |
| 354 | | be freed on multicart_close. |
| 355 | | -------------------------------------------------*/ |
| 356 | | |
| 357 | | static multicart_open_error save_ram_resources(multicart_t *cart) |
| 358 | | { |
| 359 | | const multicart_resource *resource; |
| 360 | | |
| 361 | | for (resource = cart->resources; resource != NULL; resource = resource->next) |
| 362 | | { |
| 363 | | if ((resource->type == MULTICART_RESOURCE_TYPE_RAM) && (resource->filename != NULL)) |
| 364 | | { |
| 365 | | image_battery_save_by_name(resource->filename, resource->ptr, resource->length); |
| 366 | | } |
| 367 | | } |
| 368 | | return MCERR_NONE; |
| 369 | | } |
| 370 | | |
| 371 | | /*------------------------------------------------- |
| 372 | | load_socket |
| 373 | | -------------------------------------------------*/ |
| 374 | | |
| 375 | | static multicart_open_error load_socket(multicart_load_state *state, xml_data_node *socket_node) |
| 376 | | { |
| 377 | | const char *id; |
| 378 | | const char *uses; |
| 379 | | const multicart_resource *resource; |
| 380 | | multicart_socket *socket; |
| 381 | | multicart_socket **next_socket; |
| 382 | | |
| 383 | | /* get the 'id' and 'uses' attributes; error if not present */ |
| 384 | | id = xml_get_attribute_string(socket_node, "id", NULL); |
| 385 | | uses = xml_get_attribute_string(socket_node, "uses", NULL); |
| 386 | | if ((id == NULL) || (uses == NULL)) |
| 387 | | return MCERR_XML_ERROR; |
| 388 | | |
| 389 | | /* find the resource */ |
| 390 | | for (resource = state->multicart->resources; resource != NULL; resource = resource->next) |
| 391 | | { |
| 392 | | if (!strcmp(uses, resource->id)) |
| 393 | | break; |
| 394 | | } |
| 395 | | if (resource == NULL) |
| 396 | | return MCERR_INVALID_RESOURCE_REF; |
| 397 | | |
| 398 | | /* create the socket */ |
| 399 | | socket = (multicart_socket *)pool_malloc_lib(state->multicart->data->pool, sizeof(*socket)); |
| 400 | | if (socket == NULL) |
| 401 | | return MCERR_OUT_OF_MEMORY; |
| 402 | | memset(socket, 0, sizeof(*socket)); |
| 403 | | socket->resource = resource; |
| 404 | | socket->ptr = resource->ptr; |
| 405 | | |
| 406 | | /* copy id */ |
| 407 | | socket->id = pool_strdup_lib(state->multicart->data->pool, id); |
| 408 | | if (socket->id == NULL) |
| 409 | | return MCERR_OUT_OF_MEMORY; |
| 410 | | |
| 411 | | /* which pointer should I use? */ |
| 412 | | if (resource->ptr != NULL) |
| 413 | | { |
| 414 | | /* use the resource's ptr */ |
| 415 | | socket->ptr = resource->ptr; |
| 416 | | } |
| 417 | | else |
| 418 | | { |
| 419 | | /* allocate bytes for this socket */ |
| 420 | | socket->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length); |
| 421 | | if (socket->ptr == NULL) |
| 422 | | return MCERR_OUT_OF_MEMORY; |
| 423 | | |
| 424 | | /* ...and clear it */ |
| 425 | | memset(socket->ptr, 0xCD, resource->length); |
| 426 | | } |
| 427 | | |
| 428 | | /* append the resource */ |
| 429 | | for (next_socket = &state->sockets; *next_socket; next_socket = &(*next_socket)->next) |
| 430 | | ; |
| 431 | | *next_socket = socket; |
| 432 | | |
| 433 | | return MCERR_NONE; |
| 434 | | } |
| 435 | | |
| 436 | | |
| 437 | | /*------------------------------------------------- |
| 438 | | load_all_sockets |
| 439 | | -------------------------------------------------*/ |
| 440 | | |
| 441 | | static multicart_open_error load_all_sockets(multicart_load_state *state) |
| 442 | | { |
| 443 | | multicart_open_error err; |
| 444 | | xml_data_node *socket_node; |
| 445 | | |
| 446 | | for (socket_node = xml_get_sibling(state->pcb_node->child, "socket"); socket_node != NULL; |
| 447 | | socket_node = xml_get_sibling(socket_node->next, "socket")) |
| 448 | | { |
| 449 | | err = load_socket(state, socket_node); |
| 450 | | if (err != MCERR_NONE) |
| 451 | | return err; |
| 452 | | } |
| 453 | | |
| 454 | | state->multicart->sockets = state->sockets; |
| 455 | | return MCERR_NONE; |
| 456 | | } |
| 457 | | |
| 458 | | |
| 459 | | /*------------------------------------------------- |
| 460 | | multicart_open - opens a multicart |
| 461 | | -------------------------------------------------*/ |
| 462 | | |
| 463 | | multicart_open_error multicart_open(const char *filename, const char *gamedrv, multicart_load_flags load_flags, multicart_t **cart) |
| 464 | | { |
| 465 | | multicart_open_error err; |
| 466 | | zip_error ziperr; |
| 467 | | object_pool *pool; |
| 468 | | multicart_load_state state = {0, }; |
| 469 | | const zip_file_header *header; |
| 470 | | const char *pcb_type; |
| 471 | | char *layout_text = NULL; |
| 472 | | |
| 473 | | /* allocate an object pool */ |
| 474 | | pool = pool_alloc_lib(NULL); |
| 475 | | if (pool == NULL) |
| 476 | | { |
| 477 | | err = MCERR_OUT_OF_MEMORY; |
| 478 | | goto done; |
| 479 | | } |
| 480 | | |
| 481 | | /* allocate the multicart */ |
| 482 | | state.multicart = (multicart_t*)pool_malloc_lib(pool, sizeof(*state.multicart)); |
| 483 | | if (state.multicart == NULL) |
| 484 | | { |
| 485 | | err = MCERR_OUT_OF_MEMORY; |
| 486 | | goto done; |
| 487 | | } |
| 488 | | memset(state.multicart, 0, sizeof(*state.multicart)); |
| 489 | | |
| 490 | | /* allocate the multicart's private data */ |
| 491 | | state.multicart->data = (multicart_private*)pool_malloc_lib(pool, sizeof(*state.multicart->data)); |
| 492 | | if (state.multicart->data == NULL) |
| 493 | | { |
| 494 | | err = MCERR_OUT_OF_MEMORY; |
| 495 | | goto done; |
| 496 | | } |
| 497 | | memset(state.multicart->data, 0, sizeof(*state.multicart->data)); |
| 498 | | state.multicart->data->pool = pool; |
| 499 | | pool = NULL; |
| 500 | | |
| 501 | | /* open the ZIP file */ |
| 502 | | ziperr = zip_file_open(filename, &state.zip); |
| 503 | | if (ziperr != ZIPERR_NONE) |
| 504 | | { |
| 505 | | err = MCERR_NOT_MULTICART; |
| 506 | | goto done; |
| 507 | | } |
| 508 | | |
| 509 | | /* find the layout.xml file */ |
| 510 | | header = find_file(state.zip, "layout.xml"); |
| 511 | | if (header == NULL) |
| 512 | | { |
| 513 | | err = MCERR_MISSING_LAYOUT; |
| 514 | | goto done; |
| 515 | | } |
| 516 | | |
| 517 | | /* reserve space for the layout text */ |
| 518 | | layout_text = (char*)malloc(header->uncompressed_length + 1); |
| 519 | | if (layout_text == NULL) |
| 520 | | { |
| 521 | | err = MCERR_OUT_OF_MEMORY; |
| 522 | | goto done; |
| 523 | | } |
| 524 | | |
| 525 | | /* uncompress the layout text */ |
| 526 | | ziperr = zip_file_decompress(state.zip, layout_text, header->uncompressed_length); |
| 527 | | if (ziperr != ZIPERR_NONE) |
| 528 | | { |
| 529 | | err = MCERR_ZIP_ERROR; |
| 530 | | goto done; |
| 531 | | } |
| 532 | | layout_text[header->uncompressed_length] = '\0'; |
| 533 | | |
| 534 | | /* parse the layout text */ |
| 535 | | state.layout_xml = xml_string_read(layout_text, NULL); |
| 536 | | if (state.layout_xml == NULL) |
| 537 | | { |
| 538 | | err = MCERR_XML_ERROR; |
| 539 | | goto done; |
| 540 | | } |
| 541 | | |
| 542 | | /* locate the PCB node */ |
| 543 | | if (!find_pcb_and_resource_nodes(state.layout_xml, &state.pcb_node, &state.resources_node)) |
| 544 | | { |
| 545 | | err = MCERR_NO_PCB_OR_RESOURCES; |
| 546 | | goto done; |
| 547 | | } |
| 548 | | |
| 549 | | /* get the PCB resource_type */ |
| 550 | | pcb_type = xml_get_attribute_string(state.pcb_node, "type", ""); |
| 551 | | state.multicart->pcb_type = pool_strdup_lib(state.multicart->data->pool, pcb_type); |
| 552 | | if (state.multicart->pcb_type == NULL) |
| 553 | | { |
| 554 | | err = MCERR_OUT_OF_MEMORY; |
| 555 | | goto done; |
| 556 | | } |
| 557 | | |
| 558 | | state.multicart->gamedrv_name = pool_strdup_lib(state.multicart->data->pool, gamedrv); |
| 559 | | if (state.multicart->gamedrv_name == NULL) |
| 560 | | { |
| 561 | | err = MCERR_OUT_OF_MEMORY; |
| 562 | | goto done; |
| 563 | | } |
| 564 | | |
| 565 | | /* do we have to load resources? */ |
| 566 | | if (load_flags & MULTICART_FLAGS_LOAD_RESOURCES) |
| 567 | | { |
| 568 | | err = load_all_resources(&state); |
| 569 | | if (err != MCERR_NONE) |
| 570 | | goto done; |
| 571 | | |
| 572 | | err = load_all_sockets(&state); |
| 573 | | if (err != MCERR_NONE) |
| 574 | | goto done; |
| 575 | | } |
| 576 | | |
| 577 | | err = MCERR_NONE; |
| 578 | | |
| 579 | | done: |
| 580 | | if (pool != NULL) |
| 581 | | pool_free_lib(pool); |
| 582 | | if (state.zip != NULL) |
| 583 | | zip_file_close(state.zip); |
| 584 | | if (layout_text != NULL) |
| 585 | | free(layout_text); |
| 586 | | if (state.layout_xml != NULL) |
| 587 | | xml_file_free(state.layout_xml); |
| 588 | | |
| 589 | | if ((err != MCERR_NONE) && (state.multicart != NULL)) |
| 590 | | { |
| 591 | | multicart_close(state.multicart); |
| 592 | | state.multicart = NULL; |
| 593 | | } |
| 594 | | *cart = state.multicart; |
| 595 | | return err; |
| 596 | | } |
| 597 | | |
| 598 | | |
| 599 | | /*------------------------------------------------- |
| 600 | | multicart_close - closes a multicart |
| 601 | | -------------------------------------------------*/ |
| 602 | | |
| 603 | | void multicart_close(multicart_t *cart) |
| 604 | | { |
| 605 | | save_ram_resources(cart); |
| 606 | | pool_free_lib(cart->data->pool); |
| 607 | | } |
src/mess/devices/snapquik.c
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | snapquik.h |
| 4 | | |
| 5 | | Snapshots and quickloads |
| 6 | | |
| 7 | | *********************************************************************/ |
| 8 | | |
| 9 | | #include "emu.h" |
| 10 | | #include "snapquik.h" |
| 11 | | #include "z80bin.h" |
| 12 | | |
| 13 | | |
| 14 | | |
| 15 | | /*************************************************************************** |
| 16 | | TYPE DEFINITIONS |
| 17 | | ***************************************************************************/ |
| 18 | | |
| 19 | | typedef struct _snapquick_token snapquick_token; |
| 20 | | struct _snapquick_token |
| 21 | | { |
| 22 | | emu_timer *timer; |
| 23 | | snapquick_load_func load; |
| 24 | | }; |
| 25 | | |
| 26 | | |
| 27 | | |
| 28 | | /*************************************************************************** |
| 29 | | INLINE FUNCTIONS |
| 30 | | ***************************************************************************/ |
| 31 | | |
| 32 | | /*------------------------------------------------- |
| 33 | | assert_is_snapshot_or_quickload - asserts/confirms |
| 34 | | that a given device is a snapshot or quickload |
| 35 | | -------------------------------------------------*/ |
| 36 | | |
| 37 | | INLINE void assert_is_snapshot_or_quickload(device_t *device) |
| 38 | | { |
| 39 | | assert(device != NULL); |
| 40 | | assert(downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config() != NULL); |
| 41 | | assert((device->type() == SNAPSHOT) || (device->type() == QUICKLOAD) |
| 42 | | || (device->type() == Z80BIN)); |
| 43 | | } |
| 44 | | |
| 45 | | |
| 46 | | |
| 47 | | /*------------------------------------------------- |
| 48 | | get_token - safely gets the snapshot/quickload data |
| 49 | | -------------------------------------------------*/ |
| 50 | | |
| 51 | | INLINE snapquick_token *get_token(device_t *device) |
| 52 | | { |
| 53 | | assert_is_snapshot_or_quickload(device); |
| 54 | | return (snapquick_token *) downcast<legacy_device_base *>(device)->token(); |
| 55 | | } |
| 56 | | |
| 57 | | |
| 58 | | |
| 59 | | /*------------------------------------------------- |
| 60 | | get_config - safely gets the quickload config |
| 61 | | -------------------------------------------------*/ |
| 62 | | |
| 63 | | INLINE const snapquick_config *get_config(device_t *device) |
| 64 | | { |
| 65 | | assert_is_snapshot_or_quickload(device); |
| 66 | | return (const snapquick_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config(); |
| 67 | | } |
| 68 | | |
| 69 | | INLINE const snapquick_config *get_config_dev(const device_config *device) |
| 70 | | { |
| 71 | | assert(device != NULL); |
| 72 | | assert((device->type() == SNAPSHOT) || (device->type() == QUICKLOAD) |
| 73 | | || (device->type() == Z80BIN)); |
| 74 | | return (const snapquick_config *) downcast<const legacy_device_config_base *>(device)->inline_config(); |
| 75 | | } |
| 76 | | |
| 77 | | /*------------------------------------------------- |
| 78 | | log_quickload - logs and displays useful |
| 79 | | data for the end user |
| 80 | | -------------------------------------------------*/ |
| 81 | | |
| 82 | | void log_quickload(const char *type, UINT32 start, UINT32 length, UINT32 exec, const char *exec_format) |
| 83 | | { |
| 84 | | astring tempstring; |
| 85 | | |
| 86 | | logerror("Loading %04X bytes of RAM at %04X\n", length, start); |
| 87 | | |
| 88 | | tempstring.catprintf("Quickload type: %s Length: %d bytes\n", type, length); |
| 89 | | tempstring.catprintf("Start: 0x%04X End: 0x%04X Exec: ", start, start + length - 1); |
| 90 | | |
| 91 | | logerror("Quickload loaded.\n"); |
| 92 | | if (!mame_stricmp(exec_format, EXEC_NA)) |
| 93 | | tempstring.cat("N/A"); |
| 94 | | else |
| 95 | | { |
| 96 | | logerror("Execution can resume with "); |
| 97 | | logerror(exec_format, exec); |
| 98 | | logerror("\n"); |
| 99 | | tempstring.catprintf(exec_format, exec); |
| 100 | | } |
| 101 | | |
| 102 | | ui_popup_time(10, "%s", tempstring.cstr()); |
| 103 | | } |
| 104 | | |
| 105 | | /*************************************************************************** |
| 106 | | IMPLEMENTATION |
| 107 | | ***************************************************************************/ |
| 108 | | |
| 109 | | /*------------------------------------------------- |
| 110 | | TIMER_CALLBACK(process_snapshot_or_quickload) |
| 111 | | -------------------------------------------------*/ |
| 112 | | |
| 113 | | static TIMER_CALLBACK(process_snapshot_or_quickload) |
| 114 | | { |
| 115 | | device_image_interface *image = (device_image_interface *) ptr; |
| 116 | | snapquick_token *token = get_token(&image->device()); |
| 117 | | |
| 118 | | /* invoke the load */ |
| 119 | | (*token->load)(*image, |
| 120 | | image->filetype(), |
| 121 | | image->length()); |
| 122 | | |
| 123 | | /* unload the device */ |
| 124 | | image->unload(); |
| 125 | | } |
| 126 | | |
| 127 | | |
| 128 | | |
| 129 | | /*------------------------------------------------- |
| 130 | | DEVICE_START( snapquick ) |
| 131 | | -------------------------------------------------*/ |
| 132 | | |
| 133 | | static DEVICE_START( snapquick ) |
| 134 | | { |
| 135 | | snapquick_token *token = get_token(device); |
| 136 | | |
| 137 | | /* allocate a timer */ |
| 138 | | token->timer = timer_alloc(device->machine, process_snapshot_or_quickload, (void *) dynamic_cast<device_image_interface *>(device)); |
| 139 | | |
| 140 | | } |
| 141 | | |
| 142 | | |
| 143 | | |
| 144 | | /*------------------------------------------------- |
| 145 | | DEVICE_IMAGE_LOAD( snapquick ) |
| 146 | | -------------------------------------------------*/ |
| 147 | | |
| 148 | | static DEVICE_IMAGE_LOAD( snapquick ) |
| 149 | | { |
| 150 | | const snapquick_config *config = get_config(image); |
| 151 | | snapquick_token *token = get_token(image); |
| 152 | | |
| 153 | | /* locate the load function */ |
| 154 | | token->load = (snapquick_load_func) reinterpret_cast<snapquick_load_func>(image.get_device_specific_call()); |
| 155 | | |
| 156 | | /* adjust the timer */ |
| 157 | | timer_adjust_oneshot( |
| 158 | | token->timer, |
| 159 | | attotime_make(config->delay_seconds, config->delay_attoseconds), |
| 160 | | 0); |
| 161 | | |
| 162 | | return IMAGE_INIT_PASS; |
| 163 | | } |
| 164 | | |
| 165 | | |
| 166 | | |
| 167 | | /*------------------------------------------------- |
| 168 | | DEVICE_GET_INFO(snapquick) - device getinfo |
| 169 | | function |
| 170 | | -------------------------------------------------*/ |
| 171 | | |
| 172 | | static DEVICE_GET_INFO(snapquick) |
| 173 | | { |
| 174 | | switch(state) |
| 175 | | { |
| 176 | | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 177 | | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(snapquick_token); break; |
| 178 | | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(snapquick_config); break; |
| 179 | | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 180 | | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 0; break; |
| 181 | | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 0; break; |
| 182 | | |
| 183 | | /* --- the following bits of info are returned as pointers to functions --- */ |
| 184 | | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(snapquick); break; |
| 185 | | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME(snapquick); break; |
| 186 | | case DEVINFO_FCT_SNAPSHOT_QUICKLOAD_LOAD: info->f = (genf *) get_config_dev(device)->load; break; |
| 187 | | |
| 188 | | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 189 | | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 190 | | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, get_config_dev(device)->file_extensions); break; |
| 191 | | } |
| 192 | | } |
| 193 | | |
| 194 | | |
| 195 | | |
| 196 | | /*------------------------------------------------- |
| 197 | | DEVICE_GET_INFO(snapshot) - device getinfo |
| 198 | | function |
| 199 | | -------------------------------------------------*/ |
| 200 | | |
| 201 | | DEVICE_GET_INFO(snapshot) |
| 202 | | { |
| 203 | | switch(state) |
| 204 | | { |
| 205 | | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 206 | | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_SNAPSHOT; break; |
| 207 | | |
| 208 | | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 209 | | case DEVINFO_STR_NAME: strcpy(info->s, "Snapshot"); break; |
| 210 | | case DEVINFO_STR_FAMILY: strcpy(info->s, "Snapshot"); break; |
| 211 | | |
| 212 | | default: DEVICE_GET_INFO_CALL(snapquick); break; |
| 213 | | } |
| 214 | | } |
| 215 | | |
| 216 | | |
| 217 | | |
| 218 | | /*------------------------------------------------- |
| 219 | | DEVICE_GET_INFO(quickload) - device getinfo |
| 220 | | function |
| 221 | | -------------------------------------------------*/ |
| 222 | | |
| 223 | | DEVICE_GET_INFO(quickload) |
| 224 | | { |
| 225 | | switch(state) |
| 226 | | { |
| 227 | | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 228 | | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_QUICKLOAD; break; |
| 229 | | |
| 230 | | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 231 | | case DEVINFO_STR_NAME: strcpy(info->s, "Quickload"); break; |
| 232 | | case DEVINFO_STR_FAMILY: strcpy(info->s, "Quickload"); break; |
| 233 | | |
| 234 | | default: DEVICE_GET_INFO_CALL(snapquick); break; |
| 235 | | } |
| 236 | | } |
| 237 | | |
| 238 | | DEFINE_LEGACY_IMAGE_DEVICE(SNAPSHOT, snapshot); |
| 239 | | DEFINE_LEGACY_IMAGE_DEVICE(QUICKLOAD, quickload); |
src/mess/formats/flopimg.c
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | flopimg.c |
| 4 | | |
| 5 | | Floppy disk image abstraction code |
| 6 | | |
| 7 | | *********************************************************************/ |
| 8 | | |
| 9 | | #include <stdlib.h> |
| 10 | | #include <string.h> |
| 11 | | #include <stdio.h> |
| 12 | | #include <ctype.h> |
| 13 | | #include <assert.h> |
| 14 | | #include <limits.h> |
| 15 | | |
| 16 | | #include "osdepend.h" |
| 17 | | #include "formats/flopimg.h" |
| 18 | | #include "pool.h" |
| 19 | | #include "imageutl.h" |
| 20 | | |
| 21 | | #define TRACK_LOADED 0x01 |
| 22 | | #define TRACK_DIRTY 0x02 |
| 23 | | |
| 24 | | |
| 25 | | struct _floppy_image |
| 26 | | { |
| 27 | | struct io_generic io; |
| 28 | | |
| 29 | | const struct FloppyFormat *floppy_option; |
| 30 | | struct FloppyCallbacks format; |
| 31 | | |
| 32 | | /* loaded track stuff */ |
| 33 | | int loaded_track_head; |
| 34 | | int loaded_track_index; |
| 35 | | UINT32 loaded_track_size; |
| 36 | | void *loaded_track_data; |
| 37 | | UINT8 loaded_track_status; |
| 38 | | UINT8 flags; |
| 39 | | |
| 40 | | /* tagging system */ |
| 41 | | object_pool *tags; |
| 42 | | void *tag_data; |
| 43 | | }; |
| 44 | | |
| 45 | | |
| 46 | | |
| 47 | | struct _floppy_params |
| 48 | | { |
| 49 | | int param; |
| 50 | | int value; |
| 51 | | }; |
| 52 | | |
| 53 | | |
| 54 | | |
| 55 | | static floperr_t floppy_track_unload(floppy_image *floppy); |
| 56 | | |
| 57 | | OPTION_GUIDE_START(floppy_option_guide) |
| 58 | | OPTION_INT('H', "heads", "Heads") |
| 59 | | OPTION_INT('T', "tracks", "Tracks") |
| 60 | | OPTION_INT('S', "sectors", "Sectors") |
| 61 | | OPTION_INT('L', "sectorlength", "Sector Bytes") |
| 62 | | OPTION_INT('I', "interleave", "Interleave") |
| 63 | | OPTION_INT('F', "firstsectorid", "First Sector") |
| 64 | | OPTION_GUIDE_END |
| 65 | | |
| 66 | | |
| 67 | | static void floppy_close_internal(floppy_image *floppy, int close_file); |
| 68 | | |
| 69 | | /********************************************************************* |
| 70 | | opening, closing and creating of floppy images |
| 71 | | *********************************************************************/ |
| 72 | | |
| 73 | | /* basic floppy_image initialization common to floppy_open() and floppy_create() */ |
| 74 | | static floppy_image *floppy_init(void *fp, const struct io_procs *procs, int flags) |
| 75 | | { |
| 76 | | floppy_image *floppy; |
| 77 | | |
| 78 | | floppy = (floppy_image *)malloc(sizeof(struct _floppy_image)); |
| 79 | | if (!floppy) |
| 80 | | return NULL; |
| 81 | | |
| 82 | | memset(floppy, 0, sizeof(*floppy)); |
| 83 | | floppy->tags = pool_alloc_lib(NULL); |
| 84 | | floppy->tag_data = NULL; |
| 85 | | floppy->io.file = fp; |
| 86 | | floppy->io.procs = procs; |
| 87 | | floppy->io.filler = 0xFF; |
| 88 | | floppy->flags = (UINT8) flags; |
| 89 | | return floppy; |
| 90 | | } |
| 91 | | |
| 92 | | |
| 93 | | |
| 94 | | /* main code for identifying and maybe opening a disk image; not exposed |
| 95 | | * directly because this function is big and hideous */ |
| 96 | | static floperr_t floppy_open_internal(void *fp, const struct io_procs *procs, const char *extension, |
| 97 | | const struct FloppyFormat *floppy_options, int max_options, int flags, floppy_image **outfloppy, |
| 98 | | int *outoption) |
| 99 | | { |
| 100 | | floperr_t err; |
| 101 | | floppy_image *floppy; |
| 102 | | int best_option = -1; |
| 103 | | int best_vote = 0; |
| 104 | | int vote; |
| 105 | | size_t i; |
| 106 | | |
| 107 | | floppy = floppy_init(fp, procs, flags); |
| 108 | | if (!floppy) |
| 109 | | { |
| 110 | | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 111 | | goto done; |
| 112 | | } |
| 113 | | |
| 114 | | /* vote on the best format */ |
| 115 | | for (i = 0; (i < max_options) && floppy_options[i].construct; i++) |
| 116 | | { |
| 117 | | if (!extension || !floppy_options[i].extensions || image_find_extension(floppy_options[i].extensions, extension)) |
| 118 | | { |
| 119 | | if (floppy_options[i].identify) |
| 120 | | { |
| 121 | | vote = 0; |
| 122 | | err = floppy_options[i].identify(floppy, &floppy_options[i], &vote); |
| 123 | | if (err) |
| 124 | | goto done; |
| 125 | | } |
| 126 | | else |
| 127 | | { |
| 128 | | vote = 1; |
| 129 | | } |
| 130 | | |
| 131 | | /* is this option a better one? */ |
| 132 | | if (vote > best_vote) |
| 133 | | { |
| 134 | | best_vote = vote; |
| 135 | | best_option = i; |
| 136 | | } |
| 137 | | } |
| 138 | | } |
| 139 | | |
| 140 | | /* did we find a format? */ |
| 141 | | if (best_option == -1) |
| 142 | | { |
| 143 | | err = FLOPPY_ERROR_INVALIDIMAGE; |
| 144 | | goto done; |
| 145 | | } |
| 146 | | |
| 147 | | if (outfloppy) |
| 148 | | { |
| 149 | | /* call the format constructor */ |
| 150 | | err = floppy_options[best_option].construct(floppy, &floppy_options[best_option], NULL); |
| 151 | | if (err) |
| 152 | | goto done; |
| 153 | | |
| 154 | | floppy->floppy_option = &floppy_options[best_option]; |
| 155 | | } |
| 156 | | |
| 157 | | err = FLOPPY_ERROR_SUCCESS; |
| 158 | | |
| 159 | | done: |
| 160 | | /* if we have a floppy disk and we either errored or are not keeping it, close it */ |
| 161 | | if (floppy && (!outfloppy || err)) |
| 162 | | { |
| 163 | | floppy_close_internal(floppy, FALSE); |
| 164 | | floppy = NULL; |
| 165 | | } |
| 166 | | |
| 167 | | if (outoption) |
| 168 | | *outoption = err ? -1 : best_option; |
| 169 | | if (outfloppy) |
| 170 | | *outfloppy = floppy; |
| 171 | | return err; |
| 172 | | } |
| 173 | | |
| 174 | | |
| 175 | | |
| 176 | | floperr_t floppy_identify(void *fp, const struct io_procs *procs, const char *extension, |
| 177 | | const struct FloppyFormat *formats, int *identified_format) |
| 178 | | { |
| 179 | | return floppy_open_internal(fp, procs, extension, formats, INT_MAX, FLOPPY_FLAGS_READONLY, NULL, identified_format); |
| 180 | | } |
| 181 | | |
| 182 | | |
| 183 | | |
| 184 | | floperr_t floppy_open(void *fp, const struct io_procs *procs, const char *extension, |
| 185 | | const struct FloppyFormat *format, int flags, floppy_image **outfloppy) |
| 186 | | { |
| 187 | | return floppy_open_internal(fp, procs, extension, format, 1, flags, outfloppy, NULL); |
| 188 | | } |
| 189 | | |
| 190 | | |
| 191 | | |
| 192 | | floperr_t floppy_open_choices(void *fp, const struct io_procs *procs, const char *extension, |
| 193 | | const struct FloppyFormat *formats, int flags, floppy_image **outfloppy) |
| 194 | | { |
| 195 | | return floppy_open_internal(fp, procs, extension, formats, INT_MAX, flags, outfloppy, NULL); |
| 196 | | } |
| 197 | | |
| 198 | | |
| 199 | | |
| 200 | | static floperr_t option_to_floppy_error(optreserr_t oerr) |
| 201 | | { |
| 202 | | floperr_t err; |
| 203 | | switch(oerr) { |
| 204 | | case OPTIONRESOLUTION_ERROR_SUCCESS: |
| 205 | | err = FLOPPY_ERROR_SUCCESS; |
| 206 | | break; |
| 207 | | case OPTIONRESOLUTION_ERROR_OUTOFMEMORY: |
| 208 | | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 209 | | break; |
| 210 | | case OPTIONRESOLUTION_ERROR_PARAMOUTOFRANGE: |
| 211 | | case OPTIONRESOLUTION_ERROR_PARAMNOTSPECIFIED: |
| 212 | | case OPTIONRESOLUTION_ERROR_PARAMNOTFOUND: |
| 213 | | case OPTIONRESOLUTION_ERROR_PARAMALREADYSPECIFIED: |
| 214 | | case OPTIONRESOLUTION_ERROR_BADPARAM: |
| 215 | | case OPTIONRESOLUTION_ERROR_SYNTAX: |
| 216 | | default: |
| 217 | | err = FLOPPY_ERROR_INTERNAL; |
| 218 | | break; |
| 219 | | }; |
| 220 | | return err; |
| 221 | | } |
| 222 | | |
| 223 | | |
| 224 | | |
| 225 | | floperr_t floppy_create(void *fp, const struct io_procs *procs, const struct FloppyFormat *format, option_resolution *parameters, floppy_image **outfloppy) |
| 226 | | { |
| 227 | | floppy_image *floppy = NULL; |
| 228 | | optreserr_t oerr; |
| 229 | | floperr_t err; |
| 230 | | int heads, tracks, h, t; |
| 231 | | option_resolution *alloc_resolution = NULL; |
| 232 | | |
| 233 | | assert(format); |
| 234 | | |
| 235 | | /* create the new image */ |
| 236 | | floppy = floppy_init(fp, procs, 0); |
| 237 | | if (!floppy) |
| 238 | | { |
| 239 | | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 240 | | goto done; |
| 241 | | } |
| 242 | | |
| 243 | | /* if this format expects creation parameters and none were specified, create some */ |
| 244 | | if (!parameters && format->param_guidelines) |
| 245 | | { |
| 246 | | alloc_resolution = option_resolution_create(floppy_option_guide, format->param_guidelines); |
| 247 | | if (!alloc_resolution) |
| 248 | | { |
| 249 | | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 250 | | goto done; |
| 251 | | } |
| 252 | | parameters = alloc_resolution; |
| 253 | | } |
| 254 | | |
| 255 | | /* finish the parameters, if specified */ |
| 256 | | if (parameters) |
| 257 | | { |
| 258 | | oerr = option_resolution_finish(parameters); |
| 259 | | if (oerr) |
| 260 | | { |
| 261 | | err = option_to_floppy_error(oerr); |
| 262 | | goto done; |
| 263 | | } |
| 264 | | } |
| 265 | | |
| 266 | | /* call the format constructor */ |
| 267 | | err = format->construct(floppy, format, parameters); |
| 268 | | if (err) |
| 269 | | goto done; |
| 270 | | |
| 271 | | /* format the disk, ignoring if formatting not implemented */ |
| 272 | | if (floppy->format.format_track) |
| 273 | | { |
| 274 | | heads = floppy_get_heads_per_disk(floppy); |
| 275 | | tracks = floppy_get_tracks_per_disk(floppy); |
| 276 | | |
| 277 | | for (h = 0; h < heads; h++) |
| 278 | | { |
| 279 | | for (t = 0; t < tracks; t++) |
| 280 | | { |
| 281 | | err = floppy->format.format_track(floppy, h, t, parameters); |
| 282 | | if (err) |
| 283 | | goto done; |
| 284 | | } |
| 285 | | } |
| 286 | | } |
| 287 | | |
| 288 | | /* call the post_format function, if present */ |
| 289 | | if (floppy->format.post_format) |
| 290 | | { |
| 291 | | err = floppy->format.post_format(floppy, parameters); |
| 292 | | if (err) |
| 293 | | goto done; |
| 294 | | } |
| 295 | | |
| 296 | | floppy->floppy_option = format; |
| 297 | | err = FLOPPY_ERROR_SUCCESS; |
| 298 | | |
| 299 | | done: |
| 300 | | if (err && floppy) |
| 301 | | { |
| 302 | | floppy_close_internal(floppy, FALSE); |
| 303 | | floppy = NULL; |
| 304 | | } |
| 305 | | |
| 306 | | if (outfloppy) |
| 307 | | *outfloppy = floppy; |
| 308 | | else if (floppy) |
| 309 | | floppy_close_internal(floppy, FALSE); |
| 310 | | |
| 311 | | if (alloc_resolution) |
| 312 | | option_resolution_close(alloc_resolution); |
| 313 | | return err; |
| 314 | | } |
| 315 | | |
| 316 | | |
| 317 | | |
| 318 | | static void floppy_close_internal(floppy_image *floppy, int close_file) |
| 319 | | { |
| 320 | | if (floppy) { |
| 321 | | floppy_track_unload(floppy); |
| 322 | | if (close_file) |
| 323 | | io_generic_close(&floppy->io); |
| 324 | | if (floppy->loaded_track_data) |
| 325 | | free(floppy->loaded_track_data); |
| 326 | | pool_free_lib(floppy->tags); |
| 327 | | |
| 328 | | free(floppy); |
| 329 | | } |
| 330 | | } |
| 331 | | |
| 332 | | |
| 333 | | |
| 334 | | void floppy_close(floppy_image *floppy) |
| 335 | | { |
| 336 | | floppy_close_internal(floppy, TRUE); |
| 337 | | } |
| 338 | | |
| 339 | | |
| 340 | | |
| 341 | | /********************************************************************* |
| 342 | | functions useful in format constructors |
| 343 | | *********************************************************************/ |
| 344 | | |
| 345 | | struct FloppyCallbacks *floppy_callbacks(floppy_image *floppy) |
| 346 | | { |
| 347 | | assert(floppy); |
| 348 | | return &floppy->format; |
| 349 | | } |
| 350 | | |
| 351 | | |
| 352 | | |
| 353 | | void *floppy_tag(floppy_image *floppy) |
| 354 | | { |
| 355 | | assert(floppy); |
| 356 | | return floppy->tag_data; |
| 357 | | } |
| 358 | | |
| 359 | | |
| 360 | | |
| 361 | | void *floppy_create_tag(floppy_image *floppy, size_t tagsize) |
| 362 | | { |
| 363 | | floppy->tag_data = pool_malloc_lib(floppy->tags,tagsize); |
| 364 | | return floppy->tag_data; |
| 365 | | } |
| 366 | | |
| 367 | | |
| 368 | | |
| 369 | | UINT8 floppy_get_filler(floppy_image *floppy) |
| 370 | | { |
| 371 | | return floppy->io.filler; |
| 372 | | } |
| 373 | | |
| 374 | | |
| 375 | | |
| 376 | | void floppy_set_filler(floppy_image *floppy, UINT8 filler) |
| 377 | | { |
| 378 | | floppy->io.filler = filler; |
| 379 | | } |
| 380 | | |
| 381 | | |
| 382 | | |
| 383 | | /********************************************************************* |
| 384 | | calls for accessing the raw disk image |
| 385 | | *********************************************************************/ |
| 386 | | |
| 387 | | void floppy_image_read(floppy_image *floppy, void *buffer, UINT64 offset, size_t length) |
| 388 | | { |
| 389 | | io_generic_read(&floppy->io, buffer, offset, length); |
| 390 | | } |
| 391 | | |
| 392 | | |
| 393 | | |
| 394 | | void floppy_image_write(floppy_image *floppy, const void *buffer, UINT64 offset, size_t length) |
| 395 | | { |
| 396 | | io_generic_write(&floppy->io, buffer, offset, length); |
| 397 | | } |
| 398 | | |
| 399 | | |
| 400 | | |
| 401 | | void floppy_image_write_filler(floppy_image *floppy, UINT8 filler, UINT64 offset, size_t length) |
| 402 | | { |
| 403 | | io_generic_write_filler(&floppy->io, filler, offset, length); |
| 404 | | } |
| 405 | | |
| 406 | | |
| 407 | | |
| 408 | | UINT64 floppy_image_size(floppy_image *floppy) |
| 409 | | { |
| 410 | | return io_generic_size(&floppy->io); |
| 411 | | } |
| 412 | | |
| 413 | | |
| 414 | | |
| 415 | | /********************************************************************* |
| 416 | | calls for accessing disk image data |
| 417 | | *********************************************************************/ |
| 418 | | |
| 419 | | static floperr_t floppy_readwrite_sector(floppy_image *floppy, int head, int track, int sector, int offset, |
| 420 | | void *buffer, size_t buffer_len, int writing, int indexed, int ddam) |
| 421 | | { |
| 422 | | floperr_t err; |
| 423 | | const struct FloppyCallbacks *fmt; |
| 424 | | size_t this_buffer_len; |
| 425 | | UINT8 *alloc_buf = NULL; |
| 426 | | UINT32 sector_length; |
| 427 | | UINT8 *buffer_ptr = (UINT8 *)buffer; |
| 428 | | floperr_t (*read_sector)(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen); |
| 429 | | floperr_t (*write_sector)(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam); |
| 430 | | |
| 431 | | fmt = floppy_callbacks(floppy); |
| 432 | | |
| 433 | | /* choose proper calls for indexed vs non-indexed */ |
| 434 | | if (indexed) |
| 435 | | { |
| 436 | | read_sector = fmt->read_indexed_sector; |
| 437 | | write_sector = fmt->write_indexed_sector; |
| 438 | | if (!fmt->get_indexed_sector_info) |
| 439 | | { |
| 440 | | err = FLOPPY_ERROR_UNSUPPORTED; |
| 441 | | goto done; |
| 442 | | } |
| 443 | | } |
| 444 | | else |
| 445 | | { |
| 446 | | read_sector = fmt->read_sector; |
| 447 | | write_sector = fmt->write_sector; |
| 448 | | if (!fmt->get_sector_length) |
| 449 | | { |
| 450 | | err = FLOPPY_ERROR_UNSUPPORTED; |
| 451 | | goto done; |
| 452 | | } |
| 453 | | } |
| 454 | | |
| 455 | | /* check to make sure that the operation is supported */ |
| 456 | | if (!read_sector || (writing && !write_sector)) |
| 457 | | { |
| 458 | | err = FLOPPY_ERROR_UNSUPPORTED; |
| 459 | | goto done; |
| 460 | | } |
| 461 | | |
| 462 | | /* main loop */ |
| 463 | | while(buffer_len > 0) |
| 464 | | { |
| 465 | | /* find out the size of this sector */ |
| 466 | | if (indexed) |
| 467 | | err = fmt->get_indexed_sector_info(floppy, head, track, sector, NULL, NULL, NULL, §or_length, NULL); |
| 468 | | else |
| 469 | | err = fmt->get_sector_length(floppy, head, track, sector, §or_length); |
| 470 | | if (err) |
| 471 | | goto done; |
| 472 | | |
| 473 | | /* do we even do anything with this sector? */ |
| 474 | | if (offset < sector_length) |
| 475 | | { |
| 476 | | /* ok we will be doing something */ |
| 477 | | if ((offset > 0) || (buffer_len < sector_length)) |
| 478 | | { |
| 479 | | /* we will be doing an partial read/write; in other words we |
| 480 | | * will not be reading/writing a full sector */ |
| 481 | | if (alloc_buf) free(alloc_buf); |
| 482 | | alloc_buf = (UINT8*)malloc(sector_length); |
| 483 | | if (!alloc_buf) |
| 484 | | { |
| 485 | | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 486 | | goto done; |
| 487 | | } |
| 488 | | |
| 489 | | /* read the sector (we need to do this even when writing */ |
| 490 | | err = read_sector(floppy, head, track, sector, alloc_buf, sector_length); |
| 491 | | if (err) |
| 492 | | goto done; |
| 493 | | |
| 494 | | this_buffer_len = MIN(buffer_len, sector_length - offset); |
| 495 | | |
| 496 | | if (writing) |
| 497 | | { |
| 498 | | memcpy(alloc_buf + offset, buffer_ptr, this_buffer_len); |
| 499 | | |
| 500 | | err = write_sector(floppy, head, track, sector, alloc_buf, sector_length, ddam); |
| 501 | | if (err) |
| 502 | | goto done; |
| 503 | | } |
| 504 | | else |
| 505 | | { |
| 506 | | memcpy(buffer_ptr, alloc_buf + offset, this_buffer_len); |
| 507 | | } |
| 508 | | offset += this_buffer_len; |
| 509 | | offset %= sector_length; |
| 510 | | } |
| 511 | | else |
| 512 | | { |
| 513 | | this_buffer_len = sector_length; |
| 514 | | |
| 515 | | if (writing) |
| 516 | | err = write_sector(floppy, head, track, sector, buffer_ptr, sector_length, ddam); |
| 517 | | else |
| 518 | | err = read_sector(floppy, head, track, sector, buffer_ptr, sector_length); |
| 519 | | if (err) |
| 520 | | goto done; |
| 521 | | } |
| 522 | | } |
| 523 | | else |
| 524 | | { |
| 525 | | /* skip this sector */ |
| 526 | | offset -= sector_length; |
| 527 | | this_buffer_len = 0; |
| 528 | | } |
| 529 | | |
| 530 | | buffer_ptr += this_buffer_len; |
| 531 | | buffer_len -= this_buffer_len; |
| 532 | | sector++; |
| 533 | | } |
| 534 | | |
| 535 | | err = FLOPPY_ERROR_SUCCESS; |
| 536 | | |
| 537 | | done: |
| 538 | | if (alloc_buf) |
| 539 | | free(alloc_buf); |
| 540 | | return err; |
| 541 | | } |
| 542 | | |
| 543 | | |
| 544 | | |
| 545 | | floperr_t floppy_read_sector(floppy_image *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len) |
| 546 | | { |
| 547 | | return floppy_readwrite_sector(floppy, head, track, sector, offset, buffer, buffer_len, FALSE, FALSE, 0); |
| 548 | | } |
| 549 | | |
| 550 | | |
| 551 | | |
| 552 | | floperr_t floppy_write_sector(floppy_image *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam) |
| 553 | | { |
| 554 | | return floppy_readwrite_sector(floppy, head, track, sector, offset, (void *) buffer, buffer_len, TRUE, FALSE, ddam); |
| 555 | | } |
| 556 | | |
| 557 | | |
| 558 | | |
| 559 | | floperr_t floppy_read_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len) |
| 560 | | { |
| 561 | | return floppy_readwrite_sector(floppy, head, track, sector_index, offset, buffer, buffer_len, FALSE, TRUE, 0); |
| 562 | | } |
| 563 | | |
| 564 | | |
| 565 | | |
| 566 | | floperr_t floppy_write_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam) |
| 567 | | { |
| 568 | | return floppy_readwrite_sector(floppy, head, track, sector_index, offset, (void *) buffer, buffer_len, TRUE, TRUE, ddam); |
| 569 | | } |
| 570 | | |
| 571 | | |
| 572 | | static floperr_t floppy_get_track_data_offset(floppy_image *floppy, int head, int track, UINT64 *offset) |
| 573 | | { |
| 574 | | floperr_t err; |
| 575 | | const struct FloppyCallbacks *callbacks; |
| 576 | | |
| 577 | | *offset = 0; |
| 578 | | callbacks = floppy_callbacks(floppy); |
| 579 | | if (callbacks->get_track_data_offset) |
| 580 | | { |
| 581 | | err = callbacks->get_track_data_offset(floppy, head, track, offset); |
| 582 | | if (err) |
| 583 | | return err; |
| 584 | | } |
| 585 | | return FLOPPY_ERROR_SUCCESS; |
| 586 | | } |
| 587 | | |
| 588 | | |
| 589 | | |
| 590 | | static floperr_t floppy_read_track_offset(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buffer_len) |
| 591 | | { |
| 592 | | floperr_t err; |
| 593 | | const struct FloppyCallbacks *format; |
| 594 | | |
| 595 | | format = floppy_callbacks(floppy); |
| 596 | | |
| 597 | | if (!format->read_track) |
| 598 | | return FLOPPY_ERROR_UNSUPPORTED; |
| 599 | | |
| 600 | | err = floppy_track_unload(floppy); |
| 601 | | if (err) |
| 602 | | return err; |
| 603 | | |
| 604 | | err = format->read_track(floppy, head, track, offset, buffer, buffer_len); |
| 605 | | if (err) |
| 606 | | return err; |
| 607 | | |
| 608 | | return FLOPPY_ERROR_SUCCESS; |
| 609 | | } |
| 610 | | |
| 611 | | |
| 612 | | |
| 613 | | floperr_t floppy_read_track(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len) |
| 614 | | { |
| 615 | | return floppy_read_track_offset(floppy, head, track, 0, buffer, buffer_len); |
| 616 | | } |
| 617 | | |
| 618 | | |
| 619 | | |
| 620 | | floperr_t floppy_read_track_data(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len) |
| 621 | | { |
| 622 | | floperr_t err; |
| 623 | | UINT64 offset; |
| 624 | | |
| 625 | | err = floppy_get_track_data_offset(floppy, head, track, &offset); |
| 626 | | if (err) |
| 627 | | return err; |
| 628 | | |
| 629 | | return floppy_read_track_offset(floppy, head, track, offset, buffer, buffer_len); |
| 630 | | } |
| 631 | | |
| 632 | | |
| 633 | | |
| 634 | | static floperr_t floppy_write_track_offset(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buffer_len) |
| 635 | | { |
| 636 | | floperr_t err; |
| 637 | | |
| 638 | | /* track writing supported? */ |
| 639 | | if (!floppy_callbacks(floppy)->write_track) |
| 640 | | return FLOPPY_ERROR_UNSUPPORTED; |
| 641 | | |
| 642 | | /* read only? */ |
| 643 | | if (floppy->flags & FLOPPY_FLAGS_READONLY) |
| 644 | | return FLOPPY_ERROR_READONLY; |
| 645 | | |
| 646 | | err = floppy_track_unload(floppy); |
| 647 | | if (err) |
| 648 | | return err; |
| 649 | | |
| 650 | | err = floppy_callbacks(floppy)->write_track(floppy, head, track, offset, buffer, buffer_len); |
| 651 | | if (err) |
| 652 | | return err; |
| 653 | | |
| 654 | | return FLOPPY_ERROR_SUCCESS; |
| 655 | | } |
| 656 | | |
| 657 | | |
| 658 | | |
| 659 | | floperr_t floppy_write_track(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len) |
| 660 | | { |
| 661 | | return floppy_write_track_offset(floppy, head, track, 0, buffer, buffer_len); |
| 662 | | } |
| 663 | | |
| 664 | | |
| 665 | | |
| 666 | | floperr_t floppy_write_track_data(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len) |
| 667 | | { |
| 668 | | floperr_t err; |
| 669 | | UINT64 offset; |
| 670 | | |
| 671 | | err = floppy_get_track_data_offset(floppy, head, track, &offset); |
| 672 | | if (err) |
| 673 | | return err; |
| 674 | | |
| 675 | | return floppy_write_track_offset(floppy, head, track, offset, buffer, buffer_len); |
| 676 | | } |
| 677 | | |
| 678 | | |
| 679 | | |
| 680 | | floperr_t floppy_format_track(floppy_image *floppy, int head, int track, option_resolution *parameters) |
| 681 | | { |
| 682 | | floperr_t err; |
| 683 | | struct FloppyCallbacks *format; |
| 684 | | option_resolution *alloc_resolution = NULL; |
| 685 | | optreserr_t oerr; |
| 686 | | |
| 687 | | /* supported? */ |
| 688 | | format = floppy_callbacks(floppy); |
| 689 | | if (!format->format_track) |
| 690 | | { |
| 691 | | err = FLOPPY_ERROR_UNSUPPORTED; |
| 692 | | goto done; |
| 693 | | } |
| 694 | | |
| 695 | | /* create a dummy resolution; if no parameters were specified */ |
| 696 | | if (!parameters) |
| 697 | | { |
| 698 | | alloc_resolution = option_resolution_create(floppy_option_guide, floppy->floppy_option->param_guidelines); |
| 699 | | if (!alloc_resolution) |
| 700 | | { |
| 701 | | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 702 | | goto done; |
| 703 | | } |
| 704 | | parameters = alloc_resolution; |
| 705 | | } |
| 706 | | |
| 707 | | oerr = option_resolution_finish(parameters); |
| 708 | | if (oerr) |
| 709 | | { |
| 710 | | err = option_to_floppy_error(oerr); |
| 711 | | goto done; |
| 712 | | } |
| 713 | | |
| 714 | | err = format->format_track(floppy, head, track, parameters); |
| 715 | | if (err) |
| 716 | | goto done; |
| 717 | | |
| 718 | | done: |
| 719 | | if (alloc_resolution) |
| 720 | | option_resolution_close(alloc_resolution); |
| 721 | | return err; |
| 722 | | } |
| 723 | | |
| 724 | | |
| 725 | | |
| 726 | | int floppy_get_tracks_per_disk(floppy_image *floppy) |
| 727 | | { |
| 728 | | return floppy_callbacks(floppy)->get_tracks_per_disk(floppy); |
| 729 | | } |
| 730 | | |
| 731 | | |
| 732 | | |
| 733 | | int floppy_get_heads_per_disk(floppy_image *floppy) |
| 734 | | { |
| 735 | | return floppy_callbacks(floppy)->get_heads_per_disk(floppy); |
| 736 | | } |
| 737 | | |
| 738 | | |
| 739 | | |
| 740 | | UINT32 floppy_get_track_size(floppy_image *floppy, int head, int track) |
| 741 | | { |
| 742 | | const struct FloppyCallbacks *fmt; |
| 743 | | |
| 744 | | fmt = floppy_callbacks(floppy); |
| 745 | | if (!fmt->get_track_size) |
| 746 | | return 0; |
| 747 | | |
| 748 | | return fmt->get_track_size(floppy, head, track); |
| 749 | | } |
| 750 | | |
| 751 | | |
| 752 | | |
| 753 | | floperr_t floppy_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) |
| 754 | | { |
| 755 | | const struct FloppyCallbacks *fmt; |
| 756 | | |
| 757 | | fmt = floppy_callbacks(floppy); |
| 758 | | if (!fmt->get_sector_length) |
| 759 | | return FLOPPY_ERROR_UNSUPPORTED; |
| 760 | | |
| 761 | | return fmt->get_sector_length(floppy, head, track, sector, sector_length); |
| 762 | | } |
| 763 | | |
| 764 | | |
| 765 | | |
| 766 | | floperr_t floppy_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) |
| 767 | | { |
| 768 | | const struct FloppyCallbacks *fmt; |
| 769 | | |
| 770 | | fmt = floppy_callbacks(floppy); |
| 771 | | if (!fmt->get_indexed_sector_info) |
| 772 | | return FLOPPY_ERROR_UNSUPPORTED; |
| 773 | | |
| 774 | | return fmt->get_indexed_sector_info(floppy, head, track, sector_index, cylinder, side, sector, sector_length, flags); |
| 775 | | } |
| 776 | | |
| 777 | | |
| 778 | | |
| 779 | | floperr_t floppy_get_sector_count(floppy_image *floppy, int head, int track, int *sector_count) |
| 780 | | { |
| 781 | | floperr_t err; |
| 782 | | int sector_index = 0; |
| 783 | | |
| 784 | | do |
| 785 | | { |
| 786 | | err = floppy_get_indexed_sector_info(floppy, head, track, sector_index, NULL, NULL, NULL, NULL, NULL); |
| 787 | | if (!err) |
| 788 | | sector_index++; |
| 789 | | } |
| 790 | | while(!err); |
| 791 | | |
| 792 | | if (sector_index && (err == FLOPPY_ERROR_SEEKERROR)) |
| 793 | | err = FLOPPY_ERROR_SUCCESS; |
| 794 | | if (sector_count) |
| 795 | | *sector_count = err ? 0 : sector_index; |
| 796 | | return err; |
| 797 | | } |
| 798 | | |
| 799 | | |
| 800 | | |
| 801 | | int floppy_is_read_only(floppy_image *floppy) |
| 802 | | { |
| 803 | | return floppy->flags & FLOPPY_FLAGS_READONLY; |
| 804 | | } |
| 805 | | |
| 806 | | |
| 807 | | |
| 808 | | UINT8 floppy_random_byte(floppy_image *floppy) |
| 809 | | { |
| 810 | | /* can't use mame_rand(); this might not be in the core */ |
| 811 | | #ifdef rand |
| 812 | | #undef rand |
| 813 | | #endif |
| 814 | | return rand(); |
| 815 | | } |
| 816 | | |
| 817 | | |
| 818 | | |
| 819 | | /********************************************************************* |
| 820 | | calls for track based IO |
| 821 | | *********************************************************************/ |
| 822 | | |
| 823 | | floperr_t floppy_load_track(floppy_image *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length) |
| 824 | | { |
| 825 | | floperr_t err; |
| 826 | | void *new_loaded_track_data; |
| 827 | | UINT32 track_size; |
| 828 | | |
| 829 | | /* have we already loaded this track? */ |
| 830 | | if (((floppy->loaded_track_status & TRACK_LOADED) == 0) || (head != floppy->loaded_track_head) || (track != floppy->loaded_track_index)) |
| 831 | | { |
| 832 | | err = floppy_track_unload(floppy); |
| 833 | | if (err) |
| 834 | | goto error; |
| 835 | | |
| 836 | | track_size = floppy_callbacks(floppy)->get_track_size(floppy, head, track); |
| 837 | | |
| 838 | | if (floppy->loaded_track_data) free(floppy->loaded_track_data); |
| 839 | | new_loaded_track_data = malloc(track_size); |
| 840 | | if (!new_loaded_track_data) |
| 841 | | { |
| 842 | | err = FLOPPY_ERROR_OUTOFMEMORY; |
| 843 | | goto error; |
| 844 | | } |
| 845 | | |
| 846 | | floppy->loaded_track_data = new_loaded_track_data; |
| 847 | | floppy->loaded_track_size = track_size; |
| 848 | | floppy->loaded_track_head = head; |
| 849 | | floppy->loaded_track_index = track; |
| 850 | | |
| 851 | | err = floppy_callbacks(floppy)->read_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data, floppy->loaded_track_size); |
| 852 | | if (err) |
| 853 | | goto error; |
| 854 | | |
| 855 | | floppy->loaded_track_status |= TRACK_LOADED | (dirtify ? TRACK_DIRTY : 0); |
| 856 | | } |
| 857 | | else |
| 858 | | floppy->loaded_track_status |= (dirtify ? TRACK_DIRTY : 0); |
| 859 | | |
| 860 | | if (track_data) |
| 861 | | *track_data = floppy->loaded_track_data; |
| 862 | | if (track_length) |
| 863 | | *track_length = floppy->loaded_track_size; |
| 864 | | return FLOPPY_ERROR_SUCCESS; |
| 865 | | |
| 866 | | error: |
| 867 | | if (track_data) |
| 868 | | *track_data = NULL; |
| 869 | | if (track_length) |
| 870 | | *track_length = 0; |
| 871 | | return err; |
| 872 | | } |
| 873 | | |
| 874 | | |
| 875 | | |
| 876 | | static floperr_t floppy_track_unload(floppy_image *floppy) |
| 877 | | { |
| 878 | | int err; |
| 879 | | if (floppy->loaded_track_status & TRACK_DIRTY) |
| 880 | | { |
| 881 | | err = floppy_callbacks(floppy)->write_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data, floppy->loaded_track_size); |
| 882 | | if (err) |
| 883 | | return (floperr_t)err; |
| 884 | | } |
| 885 | | |
| 886 | | floppy->loaded_track_status &= ~(TRACK_LOADED | TRACK_DIRTY); |
| 887 | | return FLOPPY_ERROR_SUCCESS; |
| 888 | | } |
| 889 | | |
| 890 | | |
| 891 | | |
| 892 | | /********************************************************************* |
| 893 | | accessors for meta information about the image |
| 894 | | *********************************************************************/ |
| 895 | | |
| 896 | | const char *floppy_format_description(floppy_image *floppy) |
| 897 | | { |
| 898 | | return floppy->floppy_option->description; |
| 899 | | } |
| 900 | | |
| 901 | | |
| 902 | | |
| 903 | | /********************************************************************* |
| 904 | | misc calls |
| 905 | | *********************************************************************/ |
| 906 | | |
| 907 | | const char *floppy_error(floperr_t err) |
| 908 | | { |
| 909 | | static const char *const error_messages[] = |
| 910 | | { |
| 911 | | "The operation completed successfully", |
| 912 | | "Fatal internal error", |
| 913 | | "This operation is unsupported", |
| 914 | | "Out of memory", |
| 915 | | "Seek error", |
| 916 | | "Invalid image", |
| 917 | | "Attempted to write to read only image", |
| 918 | | "No space left on image", |
| 919 | | "Parameter out of range", |
| 920 | | "Required parameter not specified" |
| 921 | | }; |
| 922 | | |
| 923 | | if ((err < 0) || (err >= ARRAY_LENGTH(error_messages))) |
| 924 | | return NULL; |
| 925 | | return error_messages[err]; |
| 926 | | } |
| 927 | | |
| 928 | | |
| 929 | | FLOPPY_OPTIONS_START(default) |
| 930 | | FLOPPY_OPTIONS_END |
src/mess/formats/flopimg.h
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | flopimg.h |
| 4 | | |
| 5 | | Floppy disk image abstraction code |
| 6 | | |
| 7 | | *********************************************************************/ |
| 8 | | |
| 9 | | #ifndef FLOPIMG_H |
| 10 | | #define FLOPIMG_H |
| 11 | | |
| 12 | | #include "emu.h" |
| 13 | | #include "opresolv.h" |
| 14 | | |
| 15 | | |
| 16 | | /*************************************************************************** |
| 17 | | |
| 18 | | Constants |
| 19 | | |
| 20 | | ***************************************************************************/ |
| 21 | | |
| 22 | | #define FLOPPY_FLAGS_READWRITE 0 |
| 23 | | #define FLOPPY_FLAGS_READONLY 1 |
| 24 | | |
| 25 | | |
| 26 | | |
| 27 | | /*************************************************************************** |
| 28 | | |
| 29 | | Type definitions |
| 30 | | |
| 31 | | ***************************************************************************/ |
| 32 | | |
| 33 | | typedef enum |
| 34 | | { |
| 35 | | FLOPPY_ERROR_SUCCESS, /* no error */ |
| 36 | | FLOPPY_ERROR_INTERNAL, /* fatal internal error */ |
| 37 | | FLOPPY_ERROR_UNSUPPORTED, /* this operation is unsupported */ |
| 38 | | FLOPPY_ERROR_OUTOFMEMORY, /* ran out of memory */ |
| 39 | | FLOPPY_ERROR_SEEKERROR, /* attempted to seek to nonexistant location */ |
| 40 | | FLOPPY_ERROR_INVALIDIMAGE, /* this image in invalid */ |
| 41 | | FLOPPY_ERROR_READONLY, /* attempt to write to read-only image */ |
| 42 | | FLOPPY_ERROR_NOSPACE, |
| 43 | | FLOPPY_ERROR_PARAMOUTOFRANGE, |
| 44 | | FLOPPY_ERROR_PARAMNOTSPECIFIED |
| 45 | | } |
| 46 | | floperr_t; |
| 47 | | |
| 48 | | typedef struct _floppy_image floppy_image; |
| 49 | | |
| 50 | | struct FloppyCallbacks |
| 51 | | { |
| 52 | | floperr_t (*read_sector)(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen); |
| 53 | | floperr_t (*write_sector)(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam); |
| 54 | | floperr_t (*read_indexed_sector)(floppy_image *floppy, int head, int track, int sector_index, void *buffer, size_t buflen); |
| 55 | | floperr_t (*write_indexed_sector)(floppy_image *floppy, int head, int track, int sector_index, const void *buffer, size_t buflen, int ddam); |
| 56 | | floperr_t (*read_track)(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen); |
| 57 | | floperr_t (*write_track)(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen); |
| 58 | | floperr_t (*format_track)(floppy_image *floppy, int head, int track, option_resolution *params); |
| 59 | | floperr_t (*post_format)(floppy_image *floppy, option_resolution *params); |
| 60 | | int (*get_heads_per_disk)(floppy_image *floppy); |
| 61 | | int (*get_tracks_per_disk)(floppy_image *floppy); |
| 62 | | int (*get_sectors_per_track)(floppy_image *floppy, int head, int track); |
| 63 | | UINT32 (*get_track_size)(floppy_image *floppy, int head, int track); |
| 64 | | floperr_t (*get_sector_length)(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length); |
| 65 | | floperr_t (*get_indexed_sector_info)(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags); |
| 66 | | floperr_t (*get_track_data_offset)(floppy_image *floppy, int head, int track, UINT64 *offset); |
| 67 | | }; |
| 68 | | |
| 69 | | |
| 70 | | |
| 71 | | struct FloppyFormat |
| 72 | | { |
| 73 | | const char *name; |
| 74 | | const char *extensions; |
| 75 | | const char *description; |
| 76 | | floperr_t (*identify)(floppy_image *floppy, const struct FloppyFormat *format, int *vote); |
| 77 | | floperr_t (*construct)(floppy_image *floppy, const struct FloppyFormat *format, option_resolution *params); |
| 78 | | const char *param_guidelines; |
| 79 | | }; |
| 80 | | |
| 81 | | #define FLOPPY_IDENTIFY(name) floperr_t name(floppy_image *floppy, const struct FloppyFormat *format, int *vote) |
| 82 | | #define FLOPPY_CONSTRUCT(name) floperr_t name(floppy_image *floppy, const struct FloppyFormat *format, option_resolution *params) |
| 83 | | |
| 84 | | FLOPPY_IDENTIFY(td0_dsk_identify); |
| 85 | | FLOPPY_CONSTRUCT(td0_dsk_construct); |
| 86 | | |
| 87 | | FLOPPY_IDENTIFY(imd_dsk_identify); |
| 88 | | FLOPPY_CONSTRUCT(imd_dsk_construct); |
| 89 | | |
| 90 | | FLOPPY_IDENTIFY(cqm_dsk_identify); |
| 91 | | FLOPPY_CONSTRUCT(cqm_dsk_construct); |
| 92 | | |
| 93 | | FLOPPY_IDENTIFY(dsk_dsk_identify); |
| 94 | | FLOPPY_CONSTRUCT(dsk_dsk_construct); |
| 95 | | |
| 96 | | FLOPPY_IDENTIFY(d88_dsk_identify); |
| 97 | | FLOPPY_CONSTRUCT(d88_dsk_construct); |
| 98 | | |
| 99 | | FLOPPY_IDENTIFY(fdi_dsk_identify); |
| 100 | | FLOPPY_CONSTRUCT(fdi_dsk_construct); |
| 101 | | |
| 102 | | #define FLOPPY_OPTIONS_NAME(name) floppyoptions_##name |
| 103 | | |
| 104 | | #define FLOPPY_OPTIONS_START(name) \ |
| 105 | | const struct FloppyFormat floppyoptions_##name[] = \ |
| 106 | | { \ |
| 107 | | |
| 108 | | #define FLOPPY_OPTIONS_END0 \ |
| 109 | | { NULL } \ |
| 110 | | }; |
| 111 | | |
| 112 | | #define FLOPPY_OPTIONS_EXTERN(name) \ |
| 113 | | extern const struct FloppyFormat floppyoptions_##name[] \ |
| 114 | | |
| 115 | | #define FLOPPY_OPTION(name, extensions_, description_, identify_, construct_, ranges_)\ |
| 116 | | { #name, extensions_, description_, identify_, construct_, ranges_ }, \ |
| 117 | | |
| 118 | | #define FLOPPY_OPTIONS_END \ |
| 119 | | FLOPPY_OPTION( fdi, "fdi", "Formatted Disk Image", fdi_dsk_identify, fdi_dsk_construct, NULL) \ |
| 120 | | FLOPPY_OPTION( td0, "td0", "Teledisk floppy disk image", td0_dsk_identify, td0_dsk_construct, NULL) \ |
| 121 | | FLOPPY_OPTION( imd, "imd", "IMD floppy disk image", imd_dsk_identify, imd_dsk_construct, NULL) \ |
| 122 | | FLOPPY_OPTION( cqm, "cqm,dsk", "CopyQM floppy disk image", cqm_dsk_identify, cqm_dsk_construct, NULL) \ |
| 123 | | FLOPPY_OPTION( dsk, "dsk", "DSK floppy disk image", dsk_dsk_identify, dsk_dsk_construct, NULL) \ |
| 124 | | FLOPPY_OPTION( d88, "d77,d88,1dd", "D88 Floppy Disk image", d88_dsk_identify, d88_dsk_construct, NULL) \ |
| 125 | | FLOPPY_OPTIONS_END0 |
| 126 | | |
| 127 | | FLOPPY_OPTIONS_EXTERN(default); |
| 128 | | |
| 129 | | #define PARAM_END '\0' |
| 130 | | #define PARAM_HEADS 'H' |
| 131 | | #define PARAM_TRACKS 'T' |
| 132 | | #define PARAM_SECTORS 'S' |
| 133 | | #define PARAM_SECTOR_LENGTH 'L' |
| 134 | | #define PARAM_INTERLEAVE 'I' |
| 135 | | #define PARAM_FIRST_SECTOR_ID 'F' |
| 136 | | |
| 137 | | #define HEADS(range) "H" #range |
| 138 | | #define TRACKS(range) "T" #range |
| 139 | | #define SECTORS(range) "S" #range |
| 140 | | #define SECTOR_LENGTH(range) "L" #range |
| 141 | | #define INTERLEAVE(range) "I" #range |
| 142 | | #define FIRST_SECTOR_ID(range) "F" #range |
| 143 | | |
| 144 | | |
| 145 | | /*************************************************************************** |
| 146 | | |
| 147 | | Prototypes |
| 148 | | |
| 149 | | ***************************************************************************/ |
| 150 | | |
| 151 | | OPTION_GUIDE_EXTERN(floppy_option_guide); |
| 152 | | |
| 153 | | /* opening, closing and creating of floppy images */ |
| 154 | | floperr_t floppy_open(void *fp, const struct io_procs *procs, const char *extension, const struct FloppyFormat *format, int flags, floppy_image **outfloppy); |
| 155 | | floperr_t floppy_open_choices(void *fp, const struct io_procs *procs, const char *extension, const struct FloppyFormat *formats, int flags, floppy_image **outfloppy); |
| 156 | | floperr_t floppy_create(void *fp, const struct io_procs *procs, const struct FloppyFormat *format, option_resolution *parameters, floppy_image **outfloppy); |
| 157 | | void floppy_close(floppy_image *floppy); |
| 158 | | |
| 159 | | /* useful for identifying a floppy image */ |
| 160 | | floperr_t floppy_identify(void *fp, const struct io_procs *procs, const char *extension, |
| 161 | | const struct FloppyFormat *formats, int *identified_format); |
| 162 | | |
| 163 | | /* functions useful within format constructors */ |
| 164 | | void *floppy_tag(floppy_image *floppy); |
| 165 | | void *floppy_create_tag(floppy_image *floppy, size_t tagsize); |
| 166 | | struct FloppyCallbacks *floppy_callbacks(floppy_image *floppy); |
| 167 | | UINT8 floppy_get_filler(floppy_image *floppy); |
| 168 | | void floppy_set_filler(floppy_image *floppy, UINT8 filler); |
| 169 | | |
| 170 | | /* calls for accessing disk image data */ |
| 171 | | floperr_t floppy_read_sector(floppy_image *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len); |
| 172 | | floperr_t floppy_write_sector(floppy_image *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam); |
| 173 | | floperr_t floppy_read_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len); |
| 174 | | floperr_t floppy_write_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam); |
| 175 | | floperr_t floppy_read_track(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len); |
| 176 | | floperr_t floppy_write_track(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len); |
| 177 | | floperr_t floppy_read_track_data(floppy_image *floppy, int head, int track, void *buffer, size_t buffer_len); |
| 178 | | floperr_t floppy_write_track_data(floppy_image *floppy, int head, int track, const void *buffer, size_t buffer_len); |
| 179 | | floperr_t floppy_format_track(floppy_image *floppy, int head, int track, option_resolution *params); |
| 180 | | int floppy_get_tracks_per_disk(floppy_image *floppy); |
| 181 | | int floppy_get_heads_per_disk(floppy_image *floppy); |
| 182 | | UINT32 floppy_get_track_size(floppy_image *floppy, int head, int track); |
| 183 | | floperr_t floppy_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length); |
| 184 | | floperr_t floppy_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags); |
| 185 | | floperr_t floppy_get_sector_count(floppy_image *floppy, int head, int track, int *sector_count); |
| 186 | | floperr_t floppy_load_track(floppy_image *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length); |
| 187 | | int floppy_is_read_only(floppy_image *floppy); |
| 188 | | UINT8 floppy_random_byte(floppy_image *floppy); |
| 189 | | |
| 190 | | /* accessors for meta information about the image */ |
| 191 | | const char *floppy_format_description(floppy_image *floppy); |
| 192 | | |
| 193 | | /* calls for accessing the raw disk image */ |
| 194 | | void floppy_image_read(floppy_image *floppy, void *buffer, UINT64 offset, size_t length); |
| 195 | | void floppy_image_write(floppy_image *floppy, const void *buffer, UINT64 offset, size_t length); |
| 196 | | void floppy_image_write_filler(floppy_image *floppy, UINT8 filler, UINT64 offset, size_t length); |
| 197 | | UINT64 floppy_image_size(floppy_image *floppy); |
| 198 | | |
| 199 | | /* misc */ |
| 200 | | const char *floppy_error(floperr_t err); |
| 201 | | |
| 202 | | |
| 203 | | #endif /* FLOPIMG_H */ |
| 204 | | |
src/mess/formats/cassimg.c
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | cassimg.c |
| 4 | | |
| 5 | | Cassette tape image abstraction code |
| 6 | | |
| 7 | | *********************************************************************/ |
| 8 | | |
| 9 | | |
| 10 | | #include <string.h> |
| 11 | | |
| 12 | | #include "emu.h" |
| 13 | | #include "imageutl.h" |
| 14 | | #include "pool.h" |
| 15 | | #include "formats/cassimg.h" |
| 16 | | |
| 17 | | |
| 18 | | /* debugging parameters */ |
| 19 | | #define LOG_PUT_SAMPLES 0 |
| 20 | | #define DUMP_CASSETTES 0 |
| 21 | | |
| 22 | | #define SAMPLES_PER_BLOCK 0x40000 |
| 23 | | #define CASSETTE_FLAG_DIRTY 0x10000 |
| 24 | | |
| 25 | | |
| 26 | | CASSETTE_FORMATLIST_START(cassette_default_formats) |
| 27 | | CASSETTE_FORMATLIST_END |
| 28 | | |
| 29 | | |
| 30 | | |
| 31 | | /********************************************************************* |
| 32 | | helper code |
| 33 | | *********************************************************************/ |
| 34 | | |
| 35 | | static double map_double(double d, UINT64 low, UINT64 high, UINT64 value) |
| 36 | | { |
| 37 | | #if defined(_MSC_VER) && (_MSC_VER <= 1200) |
| 38 | | /* casting unsigned __int64 to double is not supported on VC6 or before */ |
| 39 | | return d * (INT64)(value - low) / (INT64)(high - low); |
| 40 | | #else |
| 41 | | return d * (value - low) / (high - low); |
| 42 | | #endif |
| 43 | | } |
| 44 | | |
| 45 | | |
| 46 | | |
| 47 | | static size_t waveform_bytes_per_sample(int waveform_flags) |
| 48 | | { |
| 49 | | return (size_t) (1 << ((waveform_flags & 0x06) / 2)); |
| 50 | | } |
| 51 | | |
| 52 | | |
| 53 | | |
| 54 | | /********************************************************************* |
| 55 | | extrapolation and interpolation |
| 56 | | *********************************************************************/ |
| 57 | | |
| 58 | | static INT32 extrapolate8(INT8 value) |
| 59 | | { |
| 60 | | return ((INT32) value) << 24; |
| 61 | | } |
| 62 | | |
| 63 | | static INT32 extrapolate16(INT16 value) |
| 64 | | { |
| 65 | | return ((INT32) value) << 16; |
| 66 | | } |
| 67 | | |
| 68 | | static INT8 interpolate8(INT32 value) |
| 69 | | { |
| 70 | | return (INT8) (value >> 24); |
| 71 | | } |
| 72 | | |
| 73 | | static INT16 interpolate16(INT32 value) |
| 74 | | { |
| 75 | | return (INT16) (value >> 16); |
| 76 | | } |
| 77 | | |
| 78 | | |
| 79 | | |
| 80 | | /********************************************************************* |
| 81 | | initialization and termination |
| 82 | | *********************************************************************/ |
| 83 | | |
| 84 | | static cassette_image *cassette_init(running_machine *machine,const struct CassetteFormat *format, void *file, const struct io_procs *procs, int flags) |
| 85 | | { |
| 86 | | cassette_image *cassette; |
| 87 | | |
| 88 | | cassette = (cassette_image *)malloc(sizeof(struct _cassette_image)); |
| 89 | | if (!cassette) |
| 90 | | return NULL; |
| 91 | | |
| 92 | | memset(cassette, 0, sizeof(*cassette)); |
| 93 | | cassette->format = format; |
| 94 | | cassette->io.file = file; |
| 95 | | cassette->io.procs = procs; |
| 96 | | cassette->flags = flags; |
| 97 | | cassette->pool = pool_alloc_lib(NULL); |
| 98 | | cassette->machine = machine; |
| 99 | | return cassette; |
| 100 | | } |
| 101 | | |
| 102 | | |
| 103 | | |
| 104 | | static void cassette_finishinit(casserr_t err, cassette_image *cassette, cassette_image **outcassette) |
| 105 | | { |
| 106 | | if (cassette && (err || !outcassette)) |
| 107 | | { |
| 108 | | cassette_close(cassette); |
| 109 | | cassette = NULL; |
| 110 | | } |
| 111 | | if (outcassette) |
| 112 | | *outcassette = cassette; |
| 113 | | } |
| 114 | | |
| 115 | | |
| 116 | | |
| 117 | | static int good_format(const struct CassetteFormat *format, const char *extension, int flags) |
| 118 | | { |
| 119 | | if (extension && !image_find_extension(format->extensions, extension)) |
| 120 | | return FALSE; |
| 121 | | if (((flags & CASSETTE_FLAG_READONLY) == 0) && !format->save) |
| 122 | | return FALSE; |
| 123 | | return TRUE; |
| 124 | | } |
| 125 | | |
| 126 | | |
| 127 | | |
| 128 | | casserr_t cassette_open_choices(running_machine *machine,void *file, const struct io_procs *procs, const char *extension, |
| 129 | | const struct CassetteFormat *const *formats, int flags, cassette_image **outcassette) |
| 130 | | { |
| 131 | | casserr_t err; |
| 132 | | cassette_image *cassette; |
| 133 | | const struct CassetteFormat *format; |
| 134 | | struct CassetteOptions opts = {0, }; |
| 135 | | int i; |
| 136 | | |
| 137 | | /* if not specified, use the dummy arguments */ |
| 138 | | if (!formats) |
| 139 | | formats = cassette_default_formats; |
| 140 | | |
| 141 | | /* create the cassette object */ |
| 142 | | cassette = cassette_init(machine, NULL, file, procs, flags); |
| 143 | | if (!cassette) |
| 144 | | { |
| 145 | | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 146 | | goto done; |
| 147 | | } |
| 148 | | |
| 149 | | /* identify the image */ |
| 150 | | format = NULL; |
| 151 | | for (i = 0; !format && formats[i]; i++) |
| 152 | | { |
| 153 | | if (good_format(formats[i], extension, flags)) |
| 154 | | { |
| 155 | | format = formats[i]; |
| 156 | | memset(&opts, 0, sizeof(opts)); |
| 157 | | err = format->identify(cassette, &opts); |
| 158 | | if (err == CASSETTE_ERROR_INVALIDIMAGE) |
| 159 | | format = NULL; |
| 160 | | else if (err) |
| 161 | | goto done; |
| 162 | | } |
| 163 | | } |
| 164 | | |
| 165 | | /* have we found a proper format */ |
| 166 | | if (!format) |
| 167 | | { |
| 168 | | err = CASSETTE_ERROR_INVALIDIMAGE; |
| 169 | | goto done; |
| 170 | | } |
| 171 | | cassette->format = format; |
| 172 | | |
| 173 | | /* read the options */ |
| 174 | | cassette->channels = opts.channels; |
| 175 | | cassette->sample_frequency = opts.sample_frequency; |
| 176 | | |
| 177 | | /* load the image */ |
| 178 | | err = format->load(cassette); |
| 179 | | if (err) |
| 180 | | goto done; |
| 181 | | |
| 182 | | /* success */ |
| 183 | | cassette->flags &= ~CASSETTE_FLAG_DIRTY; |
| 184 | | err = CASSETTE_ERROR_SUCCESS; |
| 185 | | |
| 186 | | done: |
| 187 | | cassette_finishinit(err, cassette, outcassette); |
| 188 | | return err; |
| 189 | | } |
| 190 | | |
| 191 | | |
| 192 | | |
| 193 | | casserr_t cassette_open(running_machine *machine, void *file, const struct io_procs *procs, |
| 194 | | const struct CassetteFormat *format, int flags, cassette_image **outcassette) |
| 195 | | { |
| 196 | | const struct CassetteFormat *formats[2]; |
| 197 | | formats[0] = format; |
| 198 | | formats[1] = NULL; |
| 199 | | return cassette_open_choices(machine, file, procs, NULL, formats, flags, outcassette); |
| 200 | | } |
| 201 | | |
| 202 | | |
| 203 | | |
| 204 | | casserr_t cassette_create(running_machine *machine, void *file, const struct io_procs *procs, const struct CassetteFormat *format, |
| 205 | | const struct CassetteOptions *opts, int flags, cassette_image **outcassette) |
| 206 | | { |
| 207 | | casserr_t err; |
| 208 | | cassette_image *cassette; |
| 209 | | static const struct CassetteOptions default_options = { 1, 16, 44100 }; |
| 210 | | |
| 211 | | /* cannot create to a read only image */ |
| 212 | | if (flags & CASSETTE_FLAG_READONLY) |
| 213 | | return CASSETTE_ERROR_INVALIDIMAGE; |
| 214 | | |
| 215 | | /* is this a good format? */ |
| 216 | | if (!good_format(format, NULL, flags)) |
| 217 | | return CASSETTE_ERROR_INVALIDIMAGE; |
| 218 | | |
| 219 | | /* normalize arguments */ |
| 220 | | if (!opts) |
| 221 | | opts = &default_options; |
| 222 | | |
| 223 | | /* create the cassette object */ |
| 224 | | cassette = cassette_init(machine, format, file, procs, flags); |
| 225 | | if (!cassette) |
| 226 | | { |
| 227 | | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 228 | | goto done; |
| 229 | | } |
| 230 | | |
| 231 | | /* read the options */ |
| 232 | | cassette->channels = opts->channels; |
| 233 | | cassette->sample_frequency = opts->sample_frequency; |
| 234 | | |
| 235 | | err = CASSETTE_ERROR_SUCCESS; |
| 236 | | |
| 237 | | done: |
| 238 | | cassette_finishinit(err, cassette, outcassette); |
| 239 | | return err; |
| 240 | | } |
| 241 | | |
| 242 | | |
| 243 | | |
| 244 | | static casserr_t cassette_perform_save(cassette_image *cassette) |
| 245 | | { |
| 246 | | struct CassetteInfo info; |
| 247 | | cassette_get_info(cassette, &info); |
| 248 | | return cassette->format->save(cassette, &info); |
| 249 | | } |
| 250 | | |
| 251 | | |
| 252 | | |
| 253 | | casserr_t cassette_save(cassette_image *cassette) |
| 254 | | { |
| 255 | | casserr_t err; |
| 256 | | |
| 257 | | if (!cassette->format || !cassette->format->save) |
| 258 | | return CASSETTE_ERROR_UNSUPPORTED; |
| 259 | | |
| 260 | | err = cassette_perform_save(cassette); |
| 261 | | if (err) |
| 262 | | return err; |
| 263 | | |
| 264 | | cassette->flags &= ~CASSETTE_FLAG_DIRTY; |
| 265 | | return CASSETTE_ERROR_SUCCESS; |
| 266 | | } |
| 267 | | |
| 268 | | |
| 269 | | |
| 270 | | void cassette_get_info(cassette_image *cassette, struct CassetteInfo *info) |
| 271 | | { |
| 272 | | memset(info, 0, sizeof(*info)); |
| 273 | | info->channels = cassette->channels; |
| 274 | | info->sample_count = cassette->sample_count; |
| 275 | | info->sample_frequency = cassette->sample_frequency; |
| 276 | | info->bits_per_sample = (int) waveform_bytes_per_sample(cassette->flags) * 8; |
| 277 | | } |
| 278 | | |
| 279 | | |
| 280 | | |
| 281 | | void cassette_close(cassette_image *cassette) |
| 282 | | { |
| 283 | | if (cassette) |
| 284 | | { |
| 285 | | if ((cassette->flags & CASSETTE_FLAG_DIRTY) && (cassette->flags & CASSETTE_FLAG_SAVEONEXIT)) |
| 286 | | cassette_save(cassette); |
| 287 | | pool_free_lib(cassette->pool); |
| 288 | | free(cassette); |
| 289 | | } |
| 290 | | } |
| 291 | | |
| 292 | | |
| 293 | | |
| 294 | | void cassette_change(cassette_image *cassette, void *file, const struct io_procs *procs, const struct CassetteFormat *format, int flags) |
| 295 | | { |
| 296 | | if ((flags & CASSETTE_FLAG_READONLY) == 0) |
| 297 | | flags |= CASSETTE_FLAG_DIRTY; |
| 298 | | cassette->io.file = file; |
| 299 | | cassette->io.procs = procs; |
| 300 | | cassette->format = format; |
| 301 | | cassette->flags = flags; |
| 302 | | } |
| 303 | | |
| 304 | | |
| 305 | | |
| 306 | | /********************************************************************* |
| 307 | | calls for accessing the raw cassette image |
| 308 | | *********************************************************************/ |
| 309 | | |
| 310 | | void cassette_image_read(cassette_image *cassette, void *buffer, UINT64 offset, size_t length) |
| 311 | | { |
| 312 | | io_generic_read(&cassette->io, buffer, offset, length); |
| 313 | | } |
| 314 | | |
| 315 | | |
| 316 | | |
| 317 | | void cassette_image_write(cassette_image *cassette, const void *buffer, UINT64 offset, size_t length) |
| 318 | | { |
| 319 | | io_generic_write(&cassette->io, buffer, offset, length); |
| 320 | | } |
| 321 | | |
| 322 | | |
| 323 | | |
| 324 | | UINT64 cassette_image_size(cassette_image *cassette) |
| 325 | | { |
| 326 | | return io_generic_size(&cassette->io); |
| 327 | | } |
| 328 | | |
| 329 | | |
| 330 | | |
| 331 | | /********************************************************************* |
| 332 | | waveform accesses |
| 333 | | *********************************************************************/ |
| 334 | | |
| 335 | | struct manipulation_ranges |
| 336 | | { |
| 337 | | int channel_first; |
| 338 | | int channel_last; |
| 339 | | size_t sample_first; |
| 340 | | size_t sample_last; |
| 341 | | }; |
| 342 | | |
| 343 | | |
| 344 | | |
| 345 | | static size_t my_round(double d) |
| 346 | | { |
| 347 | | size_t result; |
| 348 | | d += 0.5; |
| 349 | | result = (size_t) d; |
| 350 | | return result; |
| 351 | | } |
| 352 | | |
| 353 | | |
| 354 | | |
| 355 | | static casserr_t compute_manipulation_ranges(cassette_image *cassette, int channel, |
| 356 | | double time_index, double sample_period, struct manipulation_ranges *ranges) |
| 357 | | { |
| 358 | | if (channel < 0) |
| 359 | | { |
| 360 | | ranges->channel_first = 0; |
| 361 | | ranges->channel_last = cassette->channels - 1; |
| 362 | | } |
| 363 | | else |
| 364 | | { |
| 365 | | ranges->channel_first = channel; |
| 366 | | ranges->channel_last = channel; |
| 367 | | } |
| 368 | | |
| 369 | | ranges->sample_first = my_round(time_index * cassette->sample_frequency); |
| 370 | | ranges->sample_last = my_round((time_index + sample_period) * cassette->sample_frequency); |
| 371 | | |
| 372 | | if (ranges->sample_last > ranges->sample_first) |
| 373 | | ranges->sample_last--; |
| 374 | | |
| 375 | | return CASSETTE_ERROR_SUCCESS; |
| 376 | | } |
| 377 | | |
| 378 | | |
| 379 | | |
| 380 | | static casserr_t lookup_sample(cassette_image *cassette, int channel, size_t sample, int allocate, INT32 **ptr) |
| 381 | | { |
| 382 | | size_t sample_block; |
| 383 | | size_t sample_index; |
| 384 | | size_t sample_size; |
| 385 | | size_t new_block_count; |
| 386 | | size_t new_block_sample_count; |
| 387 | | INT32 *new_block; |
| 388 | | struct sample_block *new_blocks; |
| 389 | | struct sample_block *block; |
| 390 | | |
| 391 | | *ptr = NULL; |
| 392 | | sample_block = (sample / SAMPLES_PER_BLOCK) * cassette->channels + channel; |
| 393 | | sample_index = sample % SAMPLES_PER_BLOCK; |
| 394 | | sample_size = sizeof(block->block[0]); |
| 395 | | |
| 396 | | /* is this block beyond the edge of our waveform? */ |
| 397 | | if (sample_block >= cassette->block_count) |
| 398 | | { |
| 399 | | if (!allocate) |
| 400 | | return CASSETTE_ERROR_SUCCESS; |
| 401 | | |
| 402 | | /* allocate new blocks */ |
| 403 | | new_block_count = sample_block + 1; |
| 404 | | new_blocks = (struct sample_block *)pool_realloc_lib(cassette->pool, cassette->blocks, new_block_count * sizeof(cassette->blocks[0])); |
| 405 | | if (!new_blocks) |
| 406 | | return CASSETTE_ERROR_OUTOFMEMORY; |
| 407 | | |
| 408 | | cassette->blocks = new_blocks; |
| 409 | | memset(&cassette->blocks[cassette->block_count], 0, (new_block_count - cassette->block_count) * sizeof(cassette->blocks[0])); |
| 410 | | cassette->block_count = new_block_count; |
| 411 | | } |
| 412 | | |
| 413 | | block = &cassette->blocks[sample_block]; |
| 414 | | |
| 415 | | /* is this sample access off the current block? */ |
| 416 | | if (sample_index >= block->sample_count) |
| 417 | | { |
| 418 | | if (!allocate) |
| 419 | | return CASSETTE_ERROR_SUCCESS; |
| 420 | | |
| 421 | | new_block_sample_count = SAMPLES_PER_BLOCK; |
| 422 | | |
| 423 | | new_block = (INT32*)pool_realloc_lib(cassette->pool, block->block, new_block_sample_count * sample_size); |
| 424 | | if (!new_block) |
| 425 | | return CASSETTE_ERROR_OUTOFMEMORY; |
| 426 | | |
| 427 | | block->block = new_block; |
| 428 | | memset(&block->block[block->sample_count], 0, (new_block_sample_count - block->sample_count) * sample_size); |
| 429 | | block->sample_count = new_block_sample_count; |
| 430 | | } |
| 431 | | |
| 432 | | *ptr = &block->block[sample_index]; |
| 433 | | return CASSETTE_ERROR_SUCCESS; |
| 434 | | } |
| 435 | | |
| 436 | | |
| 437 | | |
| 438 | | /********************************************************************* |
| 439 | | waveform accesses |
| 440 | | *********************************************************************/ |
| 441 | | |
| 442 | | casserr_t cassette_get_samples(cassette_image *cassette, int channel, |
| 443 | | double time_index, double sample_period, size_t sample_count, size_t sample_bytes, |
| 444 | | void *samples, int waveform_flags) |
| 445 | | { |
| 446 | | casserr_t err; |
| 447 | | struct manipulation_ranges ranges; |
| 448 | | size_t sample_index; |
| 449 | | size_t cassette_sample_index; |
| 450 | | UINT8 *dest_ptr; |
| 451 | | const INT32 *source_ptr; |
| 452 | | double d; |
| 453 | | INT16 word; |
| 454 | | INT32 dword; |
| 455 | | INT64 sum; |
| 456 | | |
| 457 | | assert(cassette); |
| 458 | | |
| 459 | | err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges); |
| 460 | | if (err) |
| 461 | | return err; |
| 462 | | |
| 463 | | for (sample_index = 0; sample_index < sample_count; sample_index++) |
| 464 | | { |
| 465 | | sum = 0; |
| 466 | | |
| 467 | | for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++) |
| 468 | | { |
| 469 | | /* find the sample that we are putting */ |
| 470 | | d = map_double(ranges.sample_last + 1 - ranges.sample_first, 0, sample_count, sample_index) + ranges.sample_first; |
| 471 | | cassette_sample_index = (size_t) d; |
| 472 | | err = lookup_sample(cassette, channel, cassette_sample_index, TRUE, (INT32 **) &source_ptr); |
| 473 | | if (err) |
| 474 | | return err; |
| 475 | | |
| 476 | | sum += *source_ptr; |
| 477 | | } |
| 478 | | |
| 479 | | /* average out the samples */ |
| 480 | | sum /= (ranges.channel_last + 1 - ranges.channel_first); |
| 481 | | |
| 482 | | /* and write out the result */ |
| 483 | | dest_ptr = (UINT8*)samples; |
| 484 | | dest_ptr += waveform_bytes_per_sample(waveform_flags) * sample_index; |
| 485 | | switch(waveform_bytes_per_sample(waveform_flags)) |
| 486 | | { |
| 487 | | case 1: |
| 488 | | *((INT8 *) dest_ptr) = interpolate8(sum); |
| 489 | | break; |
| 490 | | case 2: |
| 491 | | word = interpolate16(sum); |
| 492 | | if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) |
| 493 | | word = FLIPENDIAN_INT16(word); |
| 494 | | *((INT16 *) dest_ptr) = word; |
| 495 | | break; |
| 496 | | case 4: |
| 497 | | dword = sum; |
| 498 | | if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) |
| 499 | | dword = FLIPENDIAN_INT32(dword); |
| 500 | | *((INT32 *) dest_ptr) = dword; |
| 501 | | break; |
| 502 | | } |
| 503 | | } |
| 504 | | return CASSETTE_ERROR_SUCCESS; |
| 505 | | } |
| 506 | | |
| 507 | | |
| 508 | | |
| 509 | | casserr_t cassette_put_samples(cassette_image *cassette, int channel, |
| 510 | | double time_index, double sample_period, size_t sample_count, size_t sample_bytes, |
| 511 | | const void *samples, int waveform_flags) |
| 512 | | { |
| 513 | | casserr_t err; |
| 514 | | struct manipulation_ranges ranges; |
| 515 | | size_t sample_index; |
| 516 | | INT32 *dest_ptr; |
| 517 | | INT32 dest_value; |
| 518 | | INT16 word; |
| 519 | | INT32 dword; |
| 520 | | const UINT8 *source_ptr; |
| 521 | | double d; |
| 522 | | |
| 523 | | if (!cassette) |
| 524 | | return CASSETTE_ERROR_SUCCESS; |
| 525 | | |
| 526 | | if (sample_period == 0) |
| 527 | | return CASSETTE_ERROR_SUCCESS; |
| 528 | | |
| 529 | | err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges); |
| 530 | | if (err) |
| 531 | | return err; |
| 532 | | |
| 533 | | if (cassette->sample_count < ranges.sample_last+1) |
| 534 | | cassette->sample_count = ranges.sample_last + 1; |
| 535 | | cassette->flags |= CASSETTE_FLAG_DIRTY; |
| 536 | | |
| 537 | | if (LOG_PUT_SAMPLES) |
| 538 | | { |
| 539 | | logerror("cassette_put_samples(): Putting samples TIME=[%2.6g..%2.6g] INDEX=[%i..%i]\n", |
| 540 | | time_index, time_index + sample_period, |
| 541 | | (int)ranges.sample_first, (int)ranges.sample_last); |
| 542 | | } |
| 543 | | |
| 544 | | for (sample_index = ranges.sample_first; sample_index <= ranges.sample_last; sample_index++) |
| 545 | | { |
| 546 | | /* figure out the source pointer */ |
| 547 | | d = map_double(sample_count, ranges.sample_first, ranges.sample_last + 1, sample_index); |
| 548 | | source_ptr = (const UINT8*)samples; |
| 549 | | source_ptr += ((size_t) d) * sample_bytes; |
| 550 | | |
| 551 | | /* compute the value that we are writing */ |
| 552 | | switch(waveform_bytes_per_sample(waveform_flags)) { |
| 553 | | case 1: |
| 554 | | dest_value = extrapolate8(*((INT8 *) source_ptr)); |
| 555 | | break; |
| 556 | | case 2: |
| 557 | | word = *((INT16 *) source_ptr); |
| 558 | | if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) |
| 559 | | word = FLIPENDIAN_INT16(word); |
| 560 | | dest_value = extrapolate16(word); |
| 561 | | break; |
| 562 | | case 4: |
| 563 | | dword = *((INT32 *) source_ptr); |
| 564 | | if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) |
| 565 | | dword = FLIPENDIAN_INT32(dword); |
| 566 | | dest_value = dword; |
| 567 | | break; |
| 568 | | default: |
| 569 | | return CASSETTE_ERROR_INTERNAL; |
| 570 | | } |
| 571 | | |
| 572 | | for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++) |
| 573 | | { |
| 574 | | /* find the sample that we are putting */ |
| 575 | | err = lookup_sample(cassette, channel, sample_index, TRUE, &dest_ptr); |
| 576 | | if (err) |
| 577 | | return err; |
| 578 | | *dest_ptr = dest_value; |
| 579 | | } |
| 580 | | } |
| 581 | | return CASSETTE_ERROR_SUCCESS; |
| 582 | | } |
| 583 | | |
| 584 | | |
| 585 | | |
| 586 | | casserr_t cassette_get_sample(cassette_image *cassette, int channel, |
| 587 | | double time_index, double sample_period, INT32 *sample) |
| 588 | | { |
| 589 | | return cassette_get_samples(cassette, channel, time_index, |
| 590 | | sample_period, 1, 0, sample, CASSETTE_WAVEFORM_32BIT); |
| 591 | | } |
| 592 | | |
| 593 | | |
| 594 | | |
| 595 | | casserr_t cassette_put_sample(cassette_image *cassette, int channel, |
| 596 | | double time_index, double sample_period, INT32 sample) |
| 597 | | { |
| 598 | | return cassette_put_samples(cassette, channel, time_index, |
| 599 | | sample_period, 1, 0, &sample, CASSETTE_WAVEFORM_32BIT); |
| 600 | | } |
| 601 | | |
| 602 | | |
| 603 | | |
| 604 | | /********************************************************************* |
| 605 | | waveform accesses to/from the raw image |
| 606 | | *********************************************************************/ |
| 607 | | |
| 608 | | casserr_t cassette_read_samples(cassette_image *cassette, int channels, double time_index, |
| 609 | | double sample_period, size_t sample_count, UINT64 offset, int waveform_flags) |
| 610 | | { |
| 611 | | casserr_t err; |
| 612 | | size_t chunk_sample_count; |
| 613 | | size_t bytes_per_sample; |
| 614 | | size_t sample_bytes; |
| 615 | | size_t samples_loaded = 0; |
| 616 | | double chunk_time_index; |
| 617 | | double chunk_sample_period; |
| 618 | | int channel; |
| 619 | | UINT8 buffer[8192]; |
| 620 | | |
| 621 | | bytes_per_sample = waveform_bytes_per_sample(waveform_flags); |
| 622 | | sample_bytes = bytes_per_sample * channels; |
| 623 | | |
| 624 | | while(samples_loaded < sample_count) |
| 625 | | { |
| 626 | | chunk_sample_count = MIN(sizeof(buffer) / sample_bytes, (sample_count - samples_loaded)); |
| 627 | | chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count); |
| 628 | | chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_loaded); |
| 629 | | |
| 630 | | cassette_image_read(cassette, buffer, offset, chunk_sample_count * sample_bytes); |
| 631 | | |
| 632 | | for (channel = 0; channel < channels; channel++) |
| 633 | | { |
| 634 | | err = cassette_put_samples(cassette, channel, chunk_time_index, chunk_sample_period, |
| 635 | | chunk_sample_count, sample_bytes, &buffer[channel * bytes_per_sample], waveform_flags); |
| 636 | | if (err) |
| 637 | | return err; |
| 638 | | } |
| 639 | | |
| 640 | | offset += chunk_sample_count * sample_bytes; |
| 641 | | samples_loaded += chunk_sample_count; |
| 642 | | } |
| 643 | | return CASSETTE_ERROR_SUCCESS; |
| 644 | | } |
| 645 | | |
| 646 | | |
| 647 | | |
| 648 | | casserr_t cassette_write_samples(cassette_image *cassette, int channels, double time_index, |
| 649 | | double sample_period, size_t sample_count, UINT64 offset, int waveform_flags) |
| 650 | | { |
| 651 | | casserr_t err; |
| 652 | | size_t chunk_sample_count; |
| 653 | | size_t bytes_per_sample; |
| 654 | | size_t sample_bytes; |
| 655 | | size_t samples_saved = 0; |
| 656 | | double chunk_time_index; |
| 657 | | double chunk_sample_period; |
| 658 | | int channel; |
| 659 | | UINT8 buffer[8192]; |
| 660 | | |
| 661 | | bytes_per_sample = waveform_bytes_per_sample(waveform_flags); |
| 662 | | sample_bytes = bytes_per_sample * channels; |
| 663 | | |
| 664 | | while(samples_saved < sample_count) |
| 665 | | { |
| 666 | | chunk_sample_count = MIN(sizeof(buffer) / sample_bytes, (sample_count - samples_saved)); |
| 667 | | chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count); |
| 668 | | chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_saved); |
| 669 | | |
| 670 | | for (channel = 0; channel < channels; channel++) |
| 671 | | { |
| 672 | | err = cassette_get_samples(cassette, channel, chunk_time_index, chunk_sample_period, |
| 673 | | chunk_sample_count, sample_bytes, &buffer[channel * bytes_per_sample], waveform_flags); |
| 674 | | if (err) |
| 675 | | return err; |
| 676 | | } |
| 677 | | |
| 678 | | cassette_image_write(cassette, buffer, offset, chunk_sample_count * sample_bytes); |
| 679 | | |
| 680 | | offset += chunk_sample_count * sample_bytes; |
| 681 | | samples_saved += chunk_sample_count; |
| 682 | | } |
| 683 | | return CASSETTE_ERROR_SUCCESS; |
| 684 | | } |
| 685 | | |
| 686 | | |
| 687 | | |
| 688 | | /********************************************************************* |
| 689 | | waveform accesses to/from the raw image |
| 690 | | *********************************************************************/ |
| 691 | | |
| 692 | | static const INT8 *choose_wave(const struct CassetteModulation *modulation, size_t *wave_bytes_length) |
| 693 | | { |
| 694 | | static const INT8 square_wave[] = { -128, 127 }; |
| 695 | | static const INT8 sine_wave[] = { 0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48 }; |
| 696 | | |
| 697 | | if (modulation->flags & CASSETTE_MODULATION_SINEWAVE) |
| 698 | | { |
| 699 | | *wave_bytes_length = ARRAY_LENGTH(sine_wave); |
| 700 | | return sine_wave; |
| 701 | | } |
| 702 | | else |
| 703 | | { |
| 704 | | *wave_bytes_length = ARRAY_LENGTH(square_wave); |
| 705 | | return square_wave; |
| 706 | | } |
| 707 | | } |
| 708 | | |
| 709 | | |
| 710 | | |
| 711 | | casserr_t cassette_modulation_identify(cassette_image *cassette, const struct CassetteModulation *modulation, |
| 712 | | struct CassetteOptions *opts) |
| 713 | | { |
| 714 | | size_t wave_bytes_length; |
| 715 | | choose_wave(modulation, &wave_bytes_length); |
| 716 | | opts->bits_per_sample = 8; |
| 717 | | opts->channels = 1; |
| 718 | | opts->sample_frequency = (UINT32) (MAX(modulation->zero_frequency_high, modulation->one_frequency_high) * wave_bytes_length * 2); |
| 719 | | return CASSETTE_ERROR_SUCCESS; |
| 720 | | } |
| 721 | | |
| 722 | | |
| 723 | | |
| 724 | | casserr_t cassette_put_modulated_data(cassette_image *cassette, int channel, double time_index, |
| 725 | | const void *data, size_t data_length, const struct CassetteModulation *modulation, |
| 726 | | double *time_displacement) |
| 727 | | { |
| 728 | | casserr_t err; |
| 729 | | const UINT8 *data_bytes = (const UINT8 *)data; |
| 730 | | const INT8 *wave_bytes; |
| 731 | | size_t wave_bytes_length; |
| 732 | | double total_displacement = 0.0; |
| 733 | | double pulse_period; |
| 734 | | double pulse_frequency; |
| 735 | | UINT8 b; |
| 736 | | int i; |
| 737 | | |
| 738 | | wave_bytes = choose_wave(modulation, &wave_bytes_length); |
| 739 | | |
| 740 | | while(data_length--) |
| 741 | | { |
| 742 | | b = *(data_bytes++); |
| 743 | | for (i = 0; i < 8; i++) |
| 744 | | { |
| 745 | | pulse_frequency = (b & (1 << i)) ? modulation->one_frequency_cannonical : modulation->zero_frequency_cannonical; |
| 746 | | pulse_period = 1 / pulse_frequency; |
| 747 | | err = cassette_put_samples(cassette, 0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, CASSETTE_WAVEFORM_8BIT); |
| 748 | | if (err) |
| 749 | | goto done; |
| 750 | | time_index += pulse_period; |
| 751 | | total_displacement += pulse_period; |
| 752 | | } |
| 753 | | } |
| 754 | | err = CASSETTE_ERROR_SUCCESS; |
| 755 | | |
| 756 | | done: |
| 757 | | if (time_displacement) |
| 758 | | *time_displacement = total_displacement; |
| 759 | | return err; |
| 760 | | } |
| 761 | | |
| 762 | | |
| 763 | | |
| 764 | | casserr_t cassette_put_modulated_filler(cassette_image *cassette, int channel, double time_index, |
| 765 | | UINT8 filler, size_t filler_length, const struct CassetteModulation *modulation, |
| 766 | | double *time_displacement) |
| 767 | | { |
| 768 | | casserr_t err; |
| 769 | | double delta; |
| 770 | | double total_displacement = 0.0; |
| 771 | | |
| 772 | | while(filler_length--) |
| 773 | | { |
| 774 | | err = cassette_put_modulated_data(cassette, channel, time_index, &filler, 1, modulation, &delta); |
| 775 | | if (err) |
| 776 | | return err; |
| 777 | | total_displacement += delta; |
| 778 | | time_index += delta; |
| 779 | | } |
| 780 | | |
| 781 | | if (time_displacement) |
| 782 | | *time_displacement = total_displacement; |
| 783 | | return CASSETTE_ERROR_SUCCESS; |
| 784 | | } |
| 785 | | |
| 786 | | |
| 787 | | |
| 788 | | casserr_t cassette_read_modulated_data(cassette_image *cassette, int channel, double time_index, |
| 789 | | UINT64 offset, UINT64 length, const struct CassetteModulation *modulation, |
| 790 | | double *time_displacement) |
| 791 | | { |
| 792 | | casserr_t err; |
| 793 | | UINT8 buffer_stack[1024]; |
| 794 | | UINT8 *buffer; |
| 795 | | UINT8 *alloc_buffer = NULL; |
| 796 | | double delta; |
| 797 | | double total_displacement = 0.0; |
| 798 | | size_t this_length; |
| 799 | | size_t buffer_length; |
| 800 | | |
| 801 | | if (length <= sizeof(buffer_stack)) |
| 802 | | { |
| 803 | | buffer = buffer_stack; |
| 804 | | buffer_length = sizeof(buffer_stack); |
| 805 | | } |
| 806 | | else |
| 807 | | { |
| 808 | | buffer_length = MIN(length, 100000); |
| 809 | | alloc_buffer = (UINT8*)malloc(buffer_length); |
| 810 | | if (!alloc_buffer) |
| 811 | | { |
| 812 | | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 813 | | goto done; |
| 814 | | } |
| 815 | | buffer = alloc_buffer; |
| 816 | | } |
| 817 | | |
| 818 | | while(length > 0) |
| 819 | | { |
| 820 | | this_length = (size_t) MIN(length, buffer_length); |
| 821 | | cassette_image_read(cassette, buffer, offset, this_length); |
| 822 | | |
| 823 | | err = cassette_put_modulated_data(cassette, channel, time_index, buffer, this_length, modulation, &delta); |
| 824 | | if (err) |
| 825 | | goto done; |
| 826 | | total_displacement += delta; |
| 827 | | time_index += delta; |
| 828 | | length -= this_length; |
| 829 | | } |
| 830 | | |
| 831 | | if (time_displacement) |
| 832 | | *time_displacement = total_displacement; |
| 833 | | err = CASSETTE_ERROR_SUCCESS; |
| 834 | | |
| 835 | | done: |
| 836 | | if (alloc_buffer) |
| 837 | | free(alloc_buffer); |
| 838 | | return err; |
| 839 | | } |
| 840 | | |
| 841 | | |
| 842 | | |
| 843 | | casserr_t cassette_put_modulated_data_bit(cassette_image *cassette, int channel, double time_index, |
| 844 | | UINT8 data, const struct CassetteModulation *modulation, |
| 845 | | double *time_displacement) |
| 846 | | { |
| 847 | | casserr_t err; |
| 848 | | const INT8 *wave_bytes; |
| 849 | | size_t wave_bytes_length; |
| 850 | | double total_displacement = 0.0; |
| 851 | | double pulse_period; |
| 852 | | double pulse_frequency; |
| 853 | | |
| 854 | | wave_bytes = choose_wave(modulation, &wave_bytes_length); |
| 855 | | |
| 856 | | pulse_frequency = (data) ? modulation->one_frequency_cannonical : modulation->zero_frequency_cannonical; |
| 857 | | pulse_period = 1 / pulse_frequency; |
| 858 | | err = cassette_put_samples(cassette, 0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, CASSETTE_WAVEFORM_8BIT); |
| 859 | | if (err) |
| 860 | | goto done; |
| 861 | | time_index += pulse_period; |
| 862 | | total_displacement += pulse_period; |
| 863 | | |
| 864 | | err = CASSETTE_ERROR_SUCCESS; |
| 865 | | |
| 866 | | done: |
| 867 | | if (time_displacement) |
| 868 | | *time_displacement = total_displacement; |
| 869 | | return err; |
| 870 | | } |
| 871 | | |
| 872 | | |
| 873 | | |
| 874 | | /********************************************************************* |
| 875 | | waveform accesses to/from the raw image |
| 876 | | *********************************************************************/ |
| 877 | | |
| 878 | | casserr_t cassette_legacy_identify(cassette_image *cassette, struct CassetteOptions *opts, |
| 879 | | const struct CassetteLegacyWaveFiller *legacy_args) |
| 880 | | { |
| 881 | | opts->channels = 1; |
| 882 | | opts->bits_per_sample = 16; |
| 883 | | opts->sample_frequency = legacy_args->sample_frequency; |
| 884 | | return CASSETTE_ERROR_SUCCESS; |
| 885 | | } |
| 886 | | |
| 887 | | |
| 888 | | |
| 889 | | casserr_t cassette_legacy_construct(cassette_image *cassette, |
| 890 | | const struct CassetteLegacyWaveFiller *legacy_args) |
| 891 | | { |
| 892 | | casserr_t err; |
| 893 | | int length; |
| 894 | | int sample_count; |
| 895 | | void *bytes = NULL; |
| 896 | | void *chunk = NULL; |
| 897 | | INT16 *samples = NULL; |
| 898 | | int pos = 0; |
| 899 | | UINT64 offset = 0; |
| 900 | | UINT64 size; |
| 901 | | struct CassetteLegacyWaveFiller args; |
| 902 | | |
| 903 | | /* sanity check the args */ |
| 904 | | assert(legacy_args->header_samples >= -1); |
| 905 | | assert(legacy_args->trailer_samples >= 0); |
| 906 | | assert(legacy_args->fill_wave); |
| 907 | | |
| 908 | | size = cassette_image_size(cassette); |
| 909 | | |
| 910 | | /* normalize the args */ |
| 911 | | args = *legacy_args; |
| 912 | | if (args.chunk_size == 0) |
| 913 | | args.chunk_size = 1; |
| 914 | | else if (args.chunk_size < 0) |
| 915 | | args.chunk_size = cassette_image_size(cassette); |
| 916 | | if (args.sample_frequency == 0) |
| 917 | | args.sample_frequency = 11025; |
| 918 | | |
| 919 | | /* allocate a buffer for the binary data */ |
| 920 | | chunk = malloc(args.chunk_size); |
| 921 | | if (!chunk) |
| 922 | | { |
| 923 | | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 924 | | goto done; |
| 925 | | } |
| 926 | | |
| 927 | | /* determine number of samples */ |
| 928 | | if (args.chunk_sample_calc) |
| 929 | | { |
| 930 | | if (size > 0x7FFFFFFF) |
| 931 | | { |
| 932 | | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 933 | | goto done; |
| 934 | | } |
| 935 | | |
| 936 | | bytes = malloc(size); |
| 937 | | if (!bytes) |
| 938 | | { |
| 939 | | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 940 | | goto done; |
| 941 | | } |
| 942 | | cassette_image_read(cassette, bytes, 0, size); |
| 943 | | sample_count = args.chunk_sample_calc((const UINT8*)bytes, (int) size); |
| 944 | | |
| 945 | | if (args.header_samples < 0) |
| 946 | | args.header_samples = sample_count; |
| 947 | | } |
| 948 | | else |
| 949 | | { |
| 950 | | sample_count = ((size + args.chunk_size - 1) / args.chunk_size) |
| 951 | | * args.chunk_samples; |
| 952 | | } |
| 953 | | sample_count += args.header_samples + args.trailer_samples; |
| 954 | | |
| 955 | | /* allocate a buffer for the completed samples */ |
| 956 | | samples = (INT16*)malloc(sample_count * sizeof(*samples)); |
| 957 | | if (!samples) |
| 958 | | { |
| 959 | | err = CASSETTE_ERROR_OUTOFMEMORY; |
| 960 | | goto done; |
| 961 | | } |
| 962 | | |
| 963 | | /* if there has to be a header */ |
| 964 | | if (args.header_samples > 0) |
| 965 | | { |
| 966 | | length = args.fill_wave(samples + pos, sample_count - pos, CODE_HEADER); |
| 967 | | if (length < 0) |
| 968 | | { |
| 969 | | err = CASSETTE_ERROR_INVALIDIMAGE; |
| 970 | | goto done; |
| 971 | | } |
| 972 | | pos += length; |
| 973 | | } |
| 974 | | |
| 975 | | /* convert the file data to samples */ |
| 976 | | while((pos < sample_count) && (offset < size)) |
| 977 | | { |
| 978 | | cassette_image_read(cassette, chunk, offset, args.chunk_size); |
| 979 | | offset += args.chunk_size; |
| 980 | | |
| 981 | | length = args.fill_wave(samples + pos, sample_count - pos, (UINT8*)chunk); |
| 982 | | if (length < 0) |
| 983 | | { |
| 984 | | err = CASSETTE_ERROR_INVALIDIMAGE; |
| 985 | | goto done; |
| 986 | | } |
| 987 | | pos += length; |
| 988 | | if (length == 0) |
| 989 | | break; |
| 990 | | } |
| 991 | | |
| 992 | | /* if there has to be a trailer */ |
| 993 | | if (args.trailer_samples > 0) |
| 994 | | { |
| 995 | | length = args.fill_wave(samples + pos, sample_count - pos, CODE_TRAILER); |
| 996 | | if (length < 0) |
| 997 | | { |
| 998 | | err = CASSETTE_ERROR_INVALIDIMAGE; |
| 999 | | goto done; |
| 1000 | | } |
| 1001 | | pos += length; |
| 1002 | | } |
| 1003 | | |
| 1004 | | /* specify the wave */ |
| 1005 | | err = cassette_put_samples(cassette, 0, 0.0, ((double) pos) / args.sample_frequency, |
| 1006 | | pos, 2, samples, CASSETTE_WAVEFORM_16BIT); |
| 1007 | | if (err) |
| 1008 | | goto done; |
| 1009 | | |
| 1010 | | /* success! */ |
| 1011 | | err = CASSETTE_ERROR_SUCCESS; |
| 1012 | | |
| 1013 | | #if DUMP_CASSETTES |
| 1014 | | cassette_dump(cassette, "C:\\TEMP\\CASDUMP.WAV"); |
| 1015 | | #endif |
| 1016 | | |
| 1017 | | done: |
| 1018 | | if (samples) |
| 1019 | | free(samples); |
| 1020 | | if (chunk) |
| 1021 | | free(chunk); |
| 1022 | | if (bytes) |
| 1023 | | free(bytes); |
| 1024 | | return err; |
| 1025 | | } |
| 1026 | | |
| 1027 | | |
| 1028 | | |
| 1029 | | /********************************************************************* |
| 1030 | | cassette_dump |
| 1031 | | |
| 1032 | | A debugging call to dump a casette image to a disk based wave file |
| 1033 | | *********************************************************************/ |
| 1034 | | |
| 1035 | | void cassette_dump(cassette_image *image, const char *filename) |
| 1036 | | { |
| 1037 | | FILE *f; |
| 1038 | | struct io_generic saved_io; |
| 1039 | | const struct CassetteFormat *saved_format; |
| 1040 | | |
| 1041 | | f = fopen(filename, "wb"); |
| 1042 | | if (!f) |
| 1043 | | return; |
| 1044 | | |
| 1045 | | memcpy(&saved_io, &image->io, sizeof(saved_io)); |
| 1046 | | saved_format = image->format; |
| 1047 | | |
| 1048 | | image->io.file = f; |
| 1049 | | image->io.procs = &stdio_ioprocs_noclose; |
| 1050 | | image->format = &wavfile_format; |
| 1051 | | cassette_perform_save(image); |
| 1052 | | |
| 1053 | | memcpy(&image->io, &saved_io, sizeof(saved_io)); |
| 1054 | | image->format = saved_format; |
| 1055 | | |
| 1056 | | fclose(f); |
| 1057 | | } |
| 1058 | | |
| 1059 | | |
src/mess/formats/cassimg.h
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | cassimg.h |
| 4 | | |
| 5 | | Cassette tape image abstraction code |
| 6 | | |
| 7 | | *********************************************************************/ |
| 8 | | |
| 9 | | #ifndef CASSIMG_H |
| 10 | | #define CASSIMG_H |
| 11 | | |
| 12 | | #include "pool.h" |
| 13 | | #include "emu.h" |
| 14 | | |
| 15 | | /*************************************************************************** |
| 16 | | |
| 17 | | Constants |
| 18 | | |
| 19 | | ***************************************************************************/ |
| 20 | | |
| 21 | | #define CASSETTE_FLAG_READWRITE 0 |
| 22 | | #define CASSETTE_FLAG_READONLY 1 |
| 23 | | #define CASSETTE_FLAG_NOSAVEONEXIT 0 |
| 24 | | #define CASSETTE_FLAG_SAVEONEXIT 2 |
| 25 | | |
| 26 | | #define CASSETTE_WAVEFORM_8BIT 0 |
| 27 | | #define CASSETTE_WAVEFORM_16BIT 2 |
| 28 | | #define CASSETTE_WAVEFORM_16BIT_FLIP 3 |
| 29 | | #define CASSETTE_WAVEFORM_32BIT 4 |
| 30 | | #define CASSETTE_WAVEFORM_32BIT_FLIP 5 |
| 31 | | #define CASSETTE_WAVEFORM_ENDIAN_FLIP 1 |
| 32 | | |
| 33 | | #define CASSETTE_MODULATION_SQUAREWAVE 0 |
| 34 | | #define CASSETTE_MODULATION_SINEWAVE 1 |
| 35 | | |
| 36 | | |
| 37 | | #ifdef LSB_FIRST |
| 38 | | #define CASSETTE_WAVEFORM_16BITBE CASSETTE_WAVEFORM_16BIT_FLIP |
| 39 | | #define CASSETTE_WAVEFORM_16BITLE CASSETTE_WAVEFORM_16BIT |
| 40 | | #define CASSETTE_WAVEFORM_32BITBE CASSETTE_WAVEFORM_32BIT_FLIP |
| 41 | | #define CASSETTE_WAVEFORM_32BITLE CASSETTE_WAVEFORM_32BIT |
| 42 | | #else |
| 43 | | #define CASSETTE_WAVEFORM_16BITBE CASSETTE_WAVEFORM_16BIT |
| 44 | | #define CASSETTE_WAVEFORM_16BITLE CASSETTE_WAVEFORM_16BIT_FLIP |
| 45 | | #define CASSETTE_WAVEFORM_32BITBE CASSETTE_WAVEFORM_32BIT |
| 46 | | #define CASSETTE_WAVEFORM_32BITLE CASSETTE_WAVEFORM_32BIT_FLIP |
| 47 | | #endif |
| 48 | | |
| 49 | | |
| 50 | | /*************************************************************************** |
| 51 | | |
| 52 | | Type definitions |
| 53 | | |
| 54 | | ***************************************************************************/ |
| 55 | | |
| 56 | | typedef enum |
| 57 | | { |
| 58 | | CASSETTE_ERROR_SUCCESS, /* no error */ |
| 59 | | CASSETTE_ERROR_INTERNAL, /* fatal internal error */ |
| 60 | | CASSETTE_ERROR_UNSUPPORTED, /* this operation is unsupported */ |
| 61 | | CASSETTE_ERROR_OUTOFMEMORY, /* ran out of memory */ |
| 62 | | CASSETTE_ERROR_INVALIDIMAGE, /* invalid image */ |
| 63 | | CASSETTE_ERROR_INVALIDARG |
| 64 | | } |
| 65 | | casserr_t; |
| 66 | | |
| 67 | | struct sample_block |
| 68 | | { |
| 69 | | INT32 *block; |
| 70 | | size_t sample_count; |
| 71 | | }; |
| 72 | | |
| 73 | | struct CassetteOptions |
| 74 | | { |
| 75 | | int channels; |
| 76 | | int bits_per_sample; |
| 77 | | UINT32 sample_frequency; |
| 78 | | }; |
| 79 | | |
| 80 | | struct CassetteInfo |
| 81 | | { |
| 82 | | int channels; |
| 83 | | int bits_per_sample; |
| 84 | | UINT32 sample_frequency; |
| 85 | | size_t sample_count; |
| 86 | | }; |
| 87 | | |
| 88 | | struct _cassette_image |
| 89 | | { |
| 90 | | const struct CassetteFormat *format; |
| 91 | | struct io_generic io; |
| 92 | | object_pool *pool; |
| 93 | | |
| 94 | | int channels; |
| 95 | | int flags; |
| 96 | | UINT32 sample_frequency; |
| 97 | | |
| 98 | | struct sample_block *blocks; |
| 99 | | size_t block_count; |
| 100 | | size_t sample_count; |
| 101 | | |
| 102 | | running_machine *machine; |
| 103 | | }; |
| 104 | | |
| 105 | | typedef struct _cassette_image cassette_image; |
| 106 | | |
| 107 | | struct CassetteFormat |
| 108 | | { |
| 109 | | const char *extensions; |
| 110 | | casserr_t (*identify)(cassette_image *cassette, struct CassetteOptions *opts); |
| 111 | | casserr_t (*load)(cassette_image *cassette); |
| 112 | | casserr_t (*save)(cassette_image *cassette, const struct CassetteInfo *info); |
| 113 | | }; |
| 114 | | |
| 115 | | /* used for the core modulation code */ |
| 116 | | struct CassetteModulation |
| 117 | | { |
| 118 | | int flags; |
| 119 | | double zero_frequency_low; |
| 120 | | double zero_frequency_cannonical; |
| 121 | | double zero_frequency_high; |
| 122 | | double one_frequency_low; |
| 123 | | double one_frequency_cannonical; |
| 124 | | double one_frequency_high; |
| 125 | | }; |
| 126 | | |
| 127 | | /* code to adapt existing legacy fill_wave functions */ |
| 128 | | struct CassetteLegacyWaveFiller |
| 129 | | { |
| 130 | | int (*fill_wave)(INT16 *, int, UINT8 *); |
| 131 | | int chunk_size; |
| 132 | | int chunk_samples; |
| 133 | | int (*chunk_sample_calc)(const UINT8 *bytes, int length); |
| 134 | | UINT32 sample_frequency; |
| 135 | | int header_samples; |
| 136 | | int trailer_samples; |
| 137 | | }; |
| 138 | | |
| 139 | | /* builtin formats */ |
| 140 | | extern const struct CassetteFormat wavfile_format; |
| 141 | | |
| 142 | | /* macros for specifying format lists */ |
| 143 | | #define CASSETTE_FORMATLIST_EXTERN(name) \ |
| 144 | | extern const struct CassetteFormat *const name[] |
| 145 | | |
| 146 | | #define CASSETTE_FORMATLIST_START(name) \ |
| 147 | | const struct CassetteFormat *const name[] = \ |
| 148 | | { \ |
| 149 | | &wavfile_format, \ |
| 150 | | |
| 151 | | #define CASSETTE_FORMAT(name) \ |
| 152 | | &(name), \ |
| 153 | | |
| 154 | | #define CASSETTE_FORMATLIST_END \ |
| 155 | | NULL \ |
| 156 | | }; |
| 157 | | |
| 158 | | CASSETTE_FORMATLIST_EXTERN(cassette_default_formats); |
| 159 | | |
| 160 | | /*************************************************************************** |
| 161 | | |
| 162 | | Prototypes |
| 163 | | |
| 164 | | ***************************************************************************/ |
| 165 | | |
| 166 | | casserr_t cassette_open(running_machine *machine,void *file, const struct io_procs *procs, |
| 167 | | const struct CassetteFormat *format, int flags, cassette_image **outcassette); |
| 168 | | casserr_t cassette_open_choices(running_machine *machine,void *file, const struct io_procs *procs, const char *extension, |
| 169 | | const struct CassetteFormat *const *formats, int flags, cassette_image **outcassette); |
| 170 | | casserr_t cassette_create(running_machine *machine,void *file, const struct io_procs *procs, const struct CassetteFormat *format, |
| 171 | | const struct CassetteOptions *opts, int flags, cassette_image **outcassette); |
| 172 | | casserr_t cassette_save(cassette_image *cassette); |
| 173 | | void cassette_close(cassette_image *cassette); |
| 174 | | void cassette_change(cassette_image *cassette, void *file, const struct io_procs *procs, const struct CassetteFormat *format, int flags); |
| 175 | | void cassette_get_info(cassette_image *cassette, struct CassetteInfo *info); |
| 176 | | |
| 177 | | /* calls for accessing the raw cassette image */ |
| 178 | | void cassette_image_read(cassette_image *cassette, void *buffer, UINT64 offset, size_t length); |
| 179 | | void cassette_image_write(cassette_image *cassette, const void *buffer, UINT64 offset, size_t length); |
| 180 | | UINT64 cassette_image_size(cassette_image *cassette); |
| 181 | | |
| 182 | | /* waveform accesses */ |
| 183 | | casserr_t cassette_get_samples(cassette_image *cassette, int channel, |
| 184 | | double time_index, double sample_period, size_t sample_count, size_t sample_bytes, |
| 185 | | void *samples, int waveform_flags); |
| 186 | | casserr_t cassette_put_samples(cassette_image *cassette, int channel, |
| 187 | | double time_index, double sample_period, size_t sample_count, size_t sample_bytes, |
| 188 | | const void *samples, int waveform_flags); |
| 189 | | casserr_t cassette_get_sample(cassette_image *cassette, int channel, |
| 190 | | double time_index, double sample_period, INT32 *sample); |
| 191 | | casserr_t cassette_put_sample(cassette_image *cassette, int channel, |
| 192 | | double time_index, double sample_period, INT32 sample); |
| 193 | | |
| 194 | | /* waveform accesses to/from the raw image */ |
| 195 | | casserr_t cassette_read_samples(cassette_image *cassette, int channels, double time_index, |
| 196 | | double sample_period, size_t sample_count, UINT64 offset, int waveform_flags); |
| 197 | | casserr_t cassette_write_samples(cassette_image *cassette, int channels, double time_index, |
| 198 | | double sample_period, size_t sample_count, UINT64 offset, int waveform_flags); |
| 199 | | |
| 200 | | /* modulation support */ |
| 201 | | casserr_t cassette_modulation_identify(cassette_image *cassette, const struct CassetteModulation *modulation, |
| 202 | | struct CassetteOptions *opts); |
| 203 | | casserr_t cassette_put_modulated_data(cassette_image *cassette, int channel, double time_index, |
| 204 | | const void *data, size_t data_length, const struct CassetteModulation *modulation, |
| 205 | | double *time_displacement); |
| 206 | | casserr_t cassette_put_modulated_filler(cassette_image *cassette, int channel, double time_index, |
| 207 | | UINT8 filler, size_t filler_length, const struct CassetteModulation *modulation, |
| 208 | | double *time_displacement); |
| 209 | | casserr_t cassette_read_modulated_data(cassette_image *cassette, int channel, double time_index, |
| 210 | | UINT64 offset, UINT64 length, const struct CassetteModulation *modulation, |
| 211 | | double *time_displacement); |
| 212 | | casserr_t cassette_put_modulated_data_bit(cassette_image *cassette, int channel, double time_index, |
| 213 | | UINT8 data, const struct CassetteModulation *modulation, |
| 214 | | double *time_displacement); |
| 215 | | |
| 216 | | /* debug calls */ |
| 217 | | void cassette_dump(cassette_image *image, const char *filename); |
| 218 | | |
| 219 | | /* legacy code support */ |
| 220 | | #define CODE_HEADER ((UINT8*)-1) |
| 221 | | #define CODE_TRAILER ((UINT8*)-2) |
| 222 | | casserr_t cassette_legacy_identify(cassette_image *cassette, struct CassetteOptions *opts, |
| 223 | | const struct CassetteLegacyWaveFiller *legacy_args); |
| 224 | | casserr_t cassette_legacy_construct(cassette_image *cassette, |
| 225 | | const struct CassetteLegacyWaveFiller *legacy_args); |
| 226 | | |
| 227 | | #endif /* CASSIMG_H */ |
src/mess/formats/wavfile.c
| r9927 | r9928 | |
| 1 | | /********************************************************************* |
| 2 | | |
| 3 | | wavfile.c |
| 4 | | |
| 5 | | Format code for wave (*.wav) files |
| 6 | | |
| 7 | | *********************************************************************/ |
| 8 | | |
| 9 | | #include <stdio.h> |
| 10 | | #include <assert.h> |
| 11 | | |
| 12 | | #include "wavfile.h" |
| 13 | | #include "cassimg.h" |
| 14 | | |
| 15 | | static const char magic1[4] = { 'R', 'I', 'F', 'F' }; |
| 16 | | static const char magic2[4] = { 'W', 'A', 'V', 'E' }; |
| 17 | | static const char format_tag_id[4] = { 'f', 'm', 't', ' ' }; |
| 18 | | static const char data_tag_id[4] = { 'd', 'a', 't', 'a' }; |
| 19 | | |
| 20 | | #define WAV_FORMAT_PCM 1 |
| 21 | | |
| 22 | | |
| 23 | | |
| 24 | | static UINT32 get_leuint32(const void *ptr) |
| 25 | | { |
| 26 | | UINT32 value; |
| 27 | | memcpy(&value, ptr, sizeof(value)); |
| 28 | | return LITTLE_ENDIANIZE_INT32(value); |
| 29 | | } |
| 30 | | |
| 31 | | |
| 32 | | |
| 33 | | static UINT16 get_leuint16(const void *ptr) |
| 34 | | { |
| 35 | | UINT16 value; |
| 36 | | memcpy(&value, ptr, sizeof(value)); |
| 37 | | return LITTLE_ENDIANIZE_INT16(value); |
| 38 | | } |
| 39 | | |
| 40 | | |
| 41 | | |
| 42 | | static void put_leuint32(void *ptr, UINT32 value) |
| 43 | | { |
| 44 | | value = LITTLE_ENDIANIZE_INT32(value); |
| 45 | | memcpy(ptr, &value, sizeof(value)); |
| 46 | | } |
| 47 | | |
| 48 | | |
| 49 | | |
| 50 | | static void put_leuint16(void *ptr, UINT16 value) |
| 51 | | { |
| 52 | | value = LITTLE_ENDIANIZE_INT16(value); |
| 53 | | memcpy(ptr, &value, sizeof(value)); |
| 54 | | } |
| 55 | | |
| 56 | | |
| 57 | | |
| 58 | | static casserr_t wavfile_process(cassette_image *cassette, struct CassetteOptions *opts, |
| 59 | | int read_waveform) |
| 60 | | { |
| 61 | | UINT8 file_header[12]; |
| 62 | | UINT8 tag_header[8]; |
| 63 | | UINT8 format_tag[16]; |
| 64 | | UINT32 stated_size; |
| 65 | | UINT64 file_size; |
| 66 | | UINT32 tag_size; |
| 67 | | UINT32 tag_samples; |
| 68 | | UINT64 offset; |
| 69 | | int format_specified = FALSE; |
| 70 | | |
| 71 | | UINT16 format_type = 0; |
| 72 | | UINT32 bytes_per_second = 0; |
| 73 | | UINT16 block_align = 0; |
| 74 | | int waveform_flags = 0; |
| 75 | | |
| 76 | | /* read header */ |
| 77 | | cassette_image_read(cassette, file_header, 0, sizeof(file_header)); |
| 78 | | offset = sizeof(file_header); |
| 79 | | |
| 80 | | /* check magic numbers */ |
| 81 | | if (memcmp(&file_header[0], magic1, 4)) |
| 82 | | return CASSETTE_ERROR_INVALIDIMAGE; |
| 83 | | if (memcmp(&file_header[8], magic2, 4)) |
| 84 | | return CASSETTE_ERROR_INVALIDIMAGE; |
| 85 | | |
| 86 | | /* read and sanity check size */ |
| 87 | | stated_size = get_leuint32(&file_header[4]) + 8; |
| 88 | | file_size = cassette_image_size(cassette); |
| 89 | | if (stated_size > file_size) |
| 90 | | stated_size = (UINT32) file_size; |
| 91 | | |
| 92 | | while(offset < stated_size) |
| 93 | | { |
| 94 | | cassette_image_read(cassette, tag_header, offset, sizeof(tag_header)); |
| 95 | | tag_size = get_leuint32(&tag_header[4]); |
| 96 | | offset += sizeof(tag_header); |
| 97 | | |
| 98 | | if (!memcmp(tag_header, format_tag_id, 4)) |
| 99 | | { |
| 100 | | /* format tag */ |
| 101 | | if (format_specified || (tag_size < sizeof(format_tag))) |
| 102 | | return CASSETTE_ERROR_INVALIDIMAGE; |
| 103 | | format_specified = TRUE; |
| 104 | | |
| 105 | | cassette_image_read(cassette, format_tag, offset, sizeof(format_tag)); |
| 106 | | |
| 107 | | format_type = get_leuint16(&format_tag[0]); |
| 108 | | opts->channels = get_leuint16(&format_tag[2]); |
| 109 | | opts->sample_frequency = get_leuint32(&format_tag[4]); |
| 110 | | bytes_per_second = get_leuint32(&format_tag[8]); |
| 111 | | block_align = get_leuint16(&format_tag[12]); |
| 112 | | opts->bits_per_sample = get_leuint16(&format_tag[14]); |
| 113 | | |
| 114 | | if (format_type != WAV_FORMAT_PCM) |
| 115 | | return CASSETTE_ERROR_INVALIDIMAGE; |
| 116 | | if (opts->sample_frequency * opts->bits_per_sample * opts->channels / 8 != bytes_per_second) |
| 117 | | return CASSETTE_ERROR_INVALIDIMAGE; |
| 118 | | |
| 119 | | switch(opts->bits_per_sample) |
| 120 | | { |
| 121 | | case 8: |
| 122 | | waveform_flags = CASSETTE_WAVEFORM_8BIT; |
| 123 | | break; |
| 124 | | case 16: |
| 125 | | waveform_flags = CASSETTE_WAVEFORM_16BITLE; |
| 126 | | break; |
| 127 | | case 32: |
| 128 | | waveform_flags = CASSETTE_WAVEFORM_32BITLE; |
| 129 | | break; |
| 130 | | default: |
| 131 | | return CASSETTE_ERROR_INVALIDIMAGE; |
| 132 | | } |
| 133 | | } |
| 134 | | else if (!memcmp(tag_header, data_tag_id, 4)) |
| 135 | | { |
| 136 | | /* data tag */ |
| 137 | | if (!format_specified) |
| 138 | | return CASSETTE_ERROR_INVALIDIMAGE; |
| 139 | | |
| 140 | | if (read_waveform) |
| 141 | | { |
| 142 | | tag_samples = tag_size / (opts->bits_per_sample / 8) / opts->channels; |
| 143 | | cassette_read_samples(cassette, opts->channels, 0.0, tag_samples / ((double) opts->sample_frequency), |
| 144 | | tag_samples, offset, waveform_flags); |
| 145 | | } |
| 146 | | } |
| 147 | | else |
| 148 | | { |
| 149 | | /* ignore other tags */ |
| 150 | | } |
| 151 | | offset += tag_size; |
| 152 | | } |
| 153 | | |
| 154 | | return CASSETTE_ERROR_SUCCESS; |
| 155 | | } |
| 156 | | |
| 157 | | |
| 158 | | |
| 159 | | static casserr_t wavfile_identify(cassette_image *cassette, struct CassetteOptions *opts) |
| 160 | | { |
| 161 | | return wavfile_process(cassette, opts, FALSE); |
| 162 | | } |
| 163 | | |
| 164 | | |
| 165 | | |
| 166 | | static casserr_t wavfile_load(cassette_image *cassette) |
| 167 | | { |
| 168 | | struct CassetteOptions opts; |
| 169 | | memset(&opts, 0, sizeof(opts)); |
| 170 | | return wavfile_process(cassette, &opts, TRUE); |
| 171 | | } |
| 172 | | |
| 173 | | |
| 174 | | |
| 175 | | static casserr_t wavfile_save(cassette_image *cassette, const struct CassetteInfo *info) |
| 176 | | { |
| 177 | | casserr_t err; |
| 178 | | UINT8 consolidated_header[12 + 8 + 16 + 8]; |
| 179 | | UINT8 *header = &consolidated_header[0]; |
| 180 | | UINT8 *format_tag_header = &consolidated_header[12]; |
| 181 | | UINT8 *format_tag_data = &consolidated_header[12 + 8]; |
| 182 | | UINT8 *data_tag_header = &consolidated_header[12 + 8 + 16]; |
| 183 | | UINT32 file_size; |
| 184 | | UINT32 bytes_per_second; |
| 185 | | UINT16 bits_per_sample; |
| 186 | | UINT32 data_size; |
| 187 | | size_t bytes_per_sample = 2; |
| 188 | | int waveform_flags = CASSETTE_WAVEFORM_16BITLE; |
| 189 | | UINT16 block_align; |
| 190 | | |
| 191 | | bits_per_sample = (UINT16) (bytes_per_sample * 8); |
| 192 | | bytes_per_second = info->sample_frequency * bytes_per_sample * info->channels; |
| 193 | | data_size = (UINT32) (info->sample_count * bytes_per_sample * info->channels); |
| 194 | | file_size = data_size + sizeof(consolidated_header) - 8; |
| 195 | | block_align = (UINT16) (bytes_per_sample * info->channels); |
| 196 | | |
| 197 | | /* set up header */ |
| 198 | | memcpy(&header[0], magic1, 4); |
| 199 | | memcpy(&header[8], magic2, 4); |
| 200 | | put_leuint32(&header[4], file_size); |
| 201 | | |
| 202 | | /* set up format tag */ |
| 203 | | memcpy(&format_tag_header[0], format_tag_id, 4); |
| 204 | | put_leuint32(&format_tag_header[4], 16); |
| 205 | | put_leuint16(&format_tag_data[0], WAV_FORMAT_PCM); |
| 206 | | put_leuint16(&format_tag_data[2], info->channels); |
| 207 | | put_leuint32(&format_tag_data[4], info->sample_frequency); |
| 208 | | put_leuint32(&format_tag_data[8], bytes_per_second); |
| 209 | | put_leuint16(&format_tag_data[12], block_align); |
| 210 | | put_leuint16(&format_tag_data[14], bits_per_sample); |
| 211 | | |
| 212 | | /* set up data tag */ |
| 213 | | memcpy(&data_tag_header[0], data_tag_id, 4); |
| 214 | | put_leuint32(&data_tag_header[4], data_size); |
| 215 | | |
| 216 | | /* write consolidated header */ |
| 217 | | cassette_image_write(cassette, consolidated_header, 0, sizeof(consolidated_header)); |
| 218 | | |
| 219 | | /* write out the actual data */ |
| 220 | | err = cassette_write_samples(cassette, info->channels, 0.0, info->sample_count |
| 221 | | / (double) info->sample_frequency, info->sample_count, sizeof(consolidated_header), |
| 222 | | waveform_flags); |
| 223 | | if (err) |
| 224 | | return err; |
| 225 | | |
| 226 | | return CASSETTE_ERROR_SUCCESS; |
| 227 | | } |
| 228 | | |
| 229 | | |
| 230 | | |
| 231 | | const struct CassetteFormat wavfile_format = |
| 232 | | { |
| 233 | | "wav", |
| 234 | | wavfile_identify, |
| 235 | | wavfile_load, |
| 236 | | wavfile_save |
| 237 | | }; |
| 238 | | |
| 239 | | |
| 240 | | |
| 241 | | /********************************************************************* |
| 242 | | wavfile_testload() |
| 243 | | |
| 244 | | This is a hokey function used to test the cassette wave loading |
| 245 | | system, specifically to test that when one loads a WAV file image |
| 246 | | that the resulting info queried will be the same data in the WAV. |
| 247 | | |
| 248 | | This code has already identified some rounding errors |
| 249 | | *********************************************************************/ |
| 250 | | |
| 251 | | #ifdef UNUSED_FUNCTION |
| 252 | | void wavfile_testload(const char *fname) |
| 253 | | { |
| 254 | | cassette_image *cassette; |
| 255 | | FILE *f; |
| 256 | | long offset; |
| 257 | | int freq, samples, i; |
| 258 | | INT32 cassamp; |
| 259 | | INT16 wavsamp; |
| 260 | | |
| 261 | | f = fopen(fname, "rb"); |
| 262 | | if (!f) |
| 263 | | return; |
| 264 | | |
| 265 | | if (cassette_open(f, &stdio_ioprocs, &wavfile_format, CASSETTE_FLAG_READONLY, &cassette)) |
| 266 | | { |
| 267 | | fclose(f); |
| 268 | | return; |
| 269 | | } |
| 270 | | |
| 271 | | offset = 44; |
| 272 | | freq = 44100; |
| 273 | | samples = 5667062; |
| 274 | | |
| 275 | | for (i = 0; i < samples; i++) |
| 276 | | { |
| 277 | | cassette_get_sample(cassette, 0, i / (double) freq, 0.0, &cassamp); |
| 278 | | |
| 279 | | fseek(f, offset + i * 2, SEEK_SET); |
| 280 | | fread(&wavsamp, 1, 2, f); |
| 281 | | assert(cassamp == (((UINT32) wavsamp) << 16)); |
| 282 | | } |
| 283 | | |
| 284 | | cassette_close(cassette); |
| 285 | | |
| 286 | | fclose(f); |
| 287 | | } |
| 288 | | #endif |
| 289 | | |
| 290 | | |
| 291 | | |