(* D64.PAS -- access (c1541 and c1571) disk images
** Copyright (c) 1995,1996 Jochen Metzinger
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2, or (at your option)
** any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*)

UNIT d64;

INTERFACE

CONST
 DISK_SIZE = 683; (* blocks on a single sided disk *)

TYPE
 BLOCK = ARRAY [0..255] OF BYTE;
  (* sector on disk image *)

PROCEDURE d64_open(filename: STRING);
(* open image file *)

PROCEDURE d64_close;
(* close image file *)

PROCEDURE d64_read(tr, sk: BYTE; VAR sector: BLOCK);
(* read block at tr/sk *)

PROCEDURE d64_write(tr, sk: BYTE; VAR sector: BLOCK);
(* write block to tr/sk *)

FUNCTION d64_double_sided: BOOLEAN;
(* is image double sided? *)

FUNCTION d64_read_only: BOOLEAN;
(* is image disk read only? *)

IMPLEMENTATION

USES global, errors;

TYPE
 geometry_entry = RECORD
   first, last: BYTE;
   blocks, offs: LongInt;
 END; (* geometry_entry *)

CONST
 geometry: ARRAY [1..4] OF geometry_entry =
  ((first: 01; last: 17; blocks: 21; offs: 000),
   (first: 18; last: 24; blocks: 19; offs: 357),
   (first: 25; last: 30; blocks: 18; offs: 490),
   (first: 31; last: 35; blocks: 17; offs: 598));

VAR
 image: FILE OF BLOCK;
 double_sided, read_only: BOOLEAN;

FUNCTION d64_double_sided: BOOLEAN;
BEGIN
 d64_double_sided := double_sided;
END; (* d64_double_sided *)

FUNCTION d64_read_only: BOOLEAN;
BEGIN
 d64_read_only := read_only;
END; (* d64_read_only *)

PROCEDURE d64_open(filename: STRING);
 VAR BAM: BLOCK; file_size: LongInt;
BEGIN
 (*$I-*)
 Assign(image, AddExt(filename,'.d64'));
 Reset(image);
 read_only := (IOResult <> 0);
 IF read_only THEN BEGIN
  FileMode := FileMode_RO;
  Reset(image);
  FileMode := FileMode_RW;
  IF IOResult <> 0 THEN BEGIN
   error('open file');
   EXIT;
  END; (* if *)
 END; (* if *)
 (*$I+*)
 file_size := FileSize(image);
 double_sided := (file_size = 2*DISK_SIZE);
 IF NOT double_sided THEN
  IF file_size <> DISK_SIZE THEN BEGIN
   Close(image);
   error('neither c1541 nor c1571 image [size]');
   EXIT;
  END; (* if *)
 d64_read(18,0,BAM);
 err_stop;
 IF (BAM[2] <> $41) OR (BAM[165] <> $32) OR (BAM[166] <> $41) THEN BEGIN
  Close(image);
  error('neither c1541 nor c1571 image [BAM]');
  EXIT;
 END; (* if *)
END; (* d64_open *)

PROCEDURE d64_close;
BEGIN
 Close(image);
END; (* d64_close *)

FUNCTION d64_position(tr, sk: BYTE): LongInt;
 VAR side_offs: LongInt; zone: 1..4;
BEGIN
 d64_position := -1;
 IF (tr = 0) OR (tr > 70) THEN
  EXIT;
 IF tr <= 35 THEN
  side_offs := 0
 ELSE BEGIN
  IF NOT double_sided THEN
   EXIT;
  side_offs := DISK_SIZE;
  Dec(tr, 35);
 END; (* else *)
 FOR zone := 1 TO 4 DO
  WITH geometry[zone] DO
   IF tr <= last THEN BEGIN
    IF sk >= blocks THEN EXIT;
    d64_position := side_offs + offs + blocks*(tr - first) + sk;
    EXIT;
   END; (* if *)
END; (* d64_position *)

PROCEDURE d64_seek(tr, sk: BYTE);
 VAR pos: LongInt;
BEGIN
 pos := d64_position(tr, sk);
 IF pos < 0 THEN BEGIN
  error('illegal track and sector '+long2str(tr,0)+' '+long2str(sk,0));
  EXIT;
 END; (* if *)
 (*$I-*)
 Seek(image, pos);
 IF IOResult <> 0 THEN FATAL('seek');
 (*$I+*)
END; (* d64_seek *)

PROCEDURE d64_read(tr, sk: BYTE; VAR sector: BLOCK);
BEGIN
 d64_seek(tr,sk);
 IF is_err THEN EXIT;
 (*$I-*)
 Read(image,sector);
 IF IOResult <> 0 THEN FATAL('read');
 (*$I+*)
END; (* d64_read *)

PROCEDURE d64_write(tr, sk: BYTE; VAR sector: BLOCK);
BEGIN
 d64_seek(tr,sk);
 IF is_err THEN EXIT;
 (*$I-*)
 Write(image,sector);
 IF IOResult <> 0 THEN error('write');
 (*$I+*)
END; (* d64_write *)

BEGIN
 double_sided := FALSE;
 read_only := TRUE;
END. (* d64 *)
