// IIgs GS/OS file I/O wrappers.
//
// GS/OS calls dispatch through $E100A8 with X holding the call number
// and a 16-bit pointer to a class-1 parameter block pushed on the
// stack.  The parm block layout is per-call but always begins with a
// pCount field giving the number of parameters used.
//
// These wrappers are STUBS — they construct the parm blocks and call
// the dispatcher, but require the caller to have a real ProDOS volume
// mounted (or equivalent GS/OS volume) for the calls to succeed.
// Without that, the dispatcher returns an error code or hangs.
//
// To use these in MAME smoke tests you'd need:
//   - A 2img / po / dsk image containing a ProDOS volume
//   - MAME launched with the image as floppy or hard-disk
//   - Tool Locator + GS/OS init via iigsToolboxInit()
// None of which the current smoke harness provides — these wrappers
// are infrastructure for a future GS/OS-aware test rig.
//
// Class-1 GS/OS calls (pCount-prefixed):
//   $2001 Create
//   $2002 Destroy
//   $2004 ChangePath
//   $2008 Volume
//   $2010 Open
//   $2012 Read
//   $2013 Write
//   $2014 Close
//   $2016 SetMark
//   $2017 GetMark
//   $2018 SetEOF
//   $2019 GetEOF
//   $2020 GetDevNumber
//   $202C DInfo
//   $2029 Quit (special — no return)
// See "GS/OS Reference" for the full ~50 calls and parm-block layouts.

#ifndef IIGS_GSOS_H
#define IIGS_GSOS_H

#ifdef __cplusplus
extern "C" {
#endif

// GS/OS string descriptor: 2-byte length + char data (no NUL).
// Use with caller-allocated storage:
//   struct { unsigned short len; char text[14]; } pname = { 6, "MYFILE" };
typedef struct {
    unsigned short length;
    char           text[1];      // variable-length; total size = length + 2
} GSString;

// Class-1 Open parm block.
typedef struct {
    unsigned short pCount;       // 2 (or up to 12 for full options)
    unsigned short refNum;       // [out] file reference number
    void          *pathname;     // [in] GSString *
    // Optional fields (if pCount > 2): requestAccess, resourceNumber,
    // accessRequested, optionList, ... — left out for the basic open.
} OpenParm;

// Class-1 Read/Write parm block.
typedef struct {
    unsigned short pCount;       // 4
    unsigned short refNum;       // [in] file reference
    void          *dataBuffer;   // [in] in-bank pointer to buffer
    unsigned long  requestCount; // [in] bytes requested
    unsigned long  transferCount;// [out] bytes actually transferred
} IORecGS;

// Class-1 Close / GetEOF / SetEOF / etc. — simple refNum-only blocks.
typedef struct {
    unsigned short pCount;       // 1
    unsigned short refNum;
} RefNumRecGS;

typedef struct {
    unsigned short pCount;       // 2
    unsigned short refNum;
    unsigned long  eof;
} EOFRecGS;

// Class-1 SetMark/GetMark parm block — 32-bit position within file.
typedef struct {
    unsigned short pCount;       // 2
    unsigned short refNum;
    unsigned long  position;     // [in for SetMark, out for GetMark]
} MarkRecGS;

// Class-1 Destroy parm block — single pathname.
typedef struct {
    unsigned short pCount;       // 1
    void          *pathname;     // [in] GSString *
} DestroyRecGS;

// Class-1 ChangePath parm block — old + new pathname (same-dir rename).
typedef struct {
    unsigned short pCount;       // 2
    void          *oldPathname;  // [in] GSString *
    void          *newPathname;  // [in] GSString *
} ChangePathRecGS;

// GS/OS ResultBuf — caller-allocated max-length buffer for routines
// that return a variable-length string (Get_Prefix, Get_Dir_Entry's
// name field).  The OS writes a 2-byte length followed by the string
// bytes (no NUL).  maxLen is the size of bufString.text + 2; if the
// answer is longer, GS/OS returns a "buffer too small" error and
// leaves bufString.length set to the required length so the caller
// can retry.
typedef struct {
    unsigned short maxLen;       // [in] sizeof(bufString) - 2
    GSString       bufString;    // [out] length + text
} ResultBuf;

// Class-1 Get_Prefix parm block ($200A).  Reads the value of a
// numbered prefix (0 = default/cwd, 8 = working directory, 1..31 =
// user prefixes).  Returns the prefix's effective pathname in
// `prefix->bufString` with length set to the actual returned length.
typedef struct {
    unsigned short pCount;       // 2
    unsigned short prefixNum;    // [in] 0..31
    void          *prefix;       // [in/out] ResultBuf *
} PrefixRecGS;

// Class-1 Get_File_Info parm block ($2006).  pCount controls which
// fields the OS fills in (callers usually set pCount=12 for full info
// or pCount=4 when they only need storageType to distinguish file
// from directory).  storageType: 1=seedling, 2=sapling, 3=tree,
// 4=Pascal area, 5=extended, 13=directory, 15=volume directory.
typedef struct {
    unsigned short pCount;       // 1..12
    void          *pathname;     // [in] GSString *
    unsigned short access;       // [out]
    unsigned short fileType;     // [out]
    unsigned long  auxType;      // [out]
    unsigned short storageType;  // [out]
    unsigned char  createDate[8];// [out]
    unsigned char  modDate[8];   // [out]
    void          *optionList;   // [out] OptionList *
    unsigned long  eof;          // [out]
    unsigned long  blocksUsed;   // [out]
    unsigned long  resourceEOF;  // [out]
    unsigned long  resourceBlocks;// [out]
} FileInfoRecGS;

// Class-1 Get_Dir_Entry parm block ($201C).  Iterates a directory
// opened via gsosOpen() — set base=0/displacement=+1 to advance to
// the next entry.  Returns $61 endOfDir when no more entries.
// `name` receives the entry's filename via the supplied ResultBuf.
typedef struct {
    unsigned short pCount;       // 1..18
    unsigned short refNum;       // [in] dir reference number
    unsigned short flags;        // [in] reserved, set 0
    unsigned short base;         // [in] 0=current, 1=first, 2=mark
    unsigned short displacement; // [in] +N entries from base
    void          *name;         // [in/out] ResultBuf *
    unsigned short entryNum;     // [out] absolute entry # within dir
    unsigned short fileType;     // [out]
    unsigned long  eof;          // [out]
    unsigned long  blockCount;   // [out]
    unsigned char  createDate[8];// [out]
    unsigned char  modDate[8];   // [out]
    unsigned short access;       // [out]
    unsigned long  auxType;      // [out]
    unsigned short fileSysID;    // [out]
    void          *optionList;   // [out] OptionList *
    unsigned long  resourceEOF;  // [out]
    unsigned long  resourceBlocks;// [out]
} DirEntryRecGS;

// Class-1 Create parm block ($2001).  Set pCount=5 to create a file or a
// directory: pathname + access + fileType + auxType + storageType.
// storageType 13 ($0D) = directory (use fileType $0F); 1 = seedling file.
typedef struct {
    unsigned short pCount;       // 5 (or up to 7 with eof / resourceEOF)
    void          *pathname;     // [in] GSString *
    unsigned short access;       // [in]
    unsigned short fileType;     // [in]
    unsigned long  auxType;      // [in]
    unsigned short storageType;  // [in] 13 = directory
    unsigned long  eof;          // [in] (pCount >= 6)
    unsigned long  resourceEOF;  // [in] (pCount >= 7)
} CreateRecGS;

// Class-1 Volume parm block ($2008).  Given a device or volume name in
// devName, returns block counts + block size; free bytes = freeBlocks *
// blockSize.  volName must point to a caller-allocated ResultBuf (the OS
// writes the volume name there), so pCount is at least 2 whenever freeBlocks
// (pCount>=4) is wanted.
typedef struct {
    unsigned short pCount;       // 6 for free-space (through blockSize)
    void          *devName;      // [in] GSString *
    void          *volName;      // [out] ResultBuf *
    unsigned long  totalBlocks;  // [out]
    unsigned long  freeBlocks;   // [out]
    unsigned short fileSysID;    // [out]
    unsigned short blockSize;    // [out]
} VolumeRecGS;

// Class-1 GetDevNumber parm block ($2020).  Maps a device OR volume name to a
// device number (used to bridge a volume name to a DInfo call).
typedef struct {
    unsigned short pCount;       // 2
    void          *devName;      // [in] GSString *  (device or volume name)
    unsigned short devNum;       // [out]
} DevNumRecGS;

// Class-1 DInfo parm block ($202C).  Given a device number, returns the device
// NAME (which is what the Volume call wants) among other characteristics.
// pCount=2 fills only devName.
typedef struct {
    unsigned short pCount;          // 2 (devNum in, devName out) .. up to 12
    unsigned short devNum;          // [in]
    void          *devName;         // [out] ResultBuf *
    unsigned short characteristics; // [out]
    unsigned long  totalBlocks;     // [out]
    unsigned short slotNum;         // [out]
    unsigned short unitNum;         // [out]
    unsigned short version;         // [out]
    unsigned short deviceID;        // [out]
    unsigned short headLink;        // [out]
    unsigned short forwardLink;     // [out]
    void          *extendedDIBPtr;  // [out]
} DInfoRecGS;

// Open / Read / Write / Close wrappers.  Each returns 0 on success or
// a non-zero GS/OS error code (see gsos.h reference for codes).  The
// parm block lives on the caller's stack; you set the input fields
// before the call and read output fields after.
//
// Implementation lives in runtime/src/iigsGsos.s — needed because the
// W65816 backend's inline asm can't take memory operands for the
// parm-block address.
extern unsigned short gsosOpen   (OpenParm    *p);
extern unsigned short gsosRead   (IORecGS     *p);
extern unsigned short gsosWrite  (IORecGS     *p);
extern unsigned short gsosClose  (RefNumRecGS *p);
extern unsigned short gsosGetEOF (EOFRecGS    *p);
extern unsigned short gsosSetEOF (EOFRecGS    *p);
extern unsigned short gsosSetMark(MarkRecGS   *p);
extern unsigned short gsosGetMark(MarkRecGS   *p);
extern unsigned short gsosDestroy   (DestroyRecGS    *p);
extern unsigned short gsosChangePath(ChangePathRecGS *p);
extern unsigned short gsosGetPrefix  (PrefixRecGS    *p);
extern unsigned short gsosGetFileInfo(FileInfoRecGS  *p);
extern unsigned short gsosGetDirEntry(DirEntryRecGS  *p);
extern unsigned short gsosCreate      (CreateRecGS   *p);
extern unsigned short gsosVolume      (VolumeRecGS   *p);
extern unsigned short gsosGetDevNumber(DevNumRecGS   *p);
extern unsigned short gsosDInfo       (DInfoRecGS    *p);

// Returns 1 when a real GS/OS dispatch surface is linked (either
// iigsGsos.o for bare-metal or libcGno.o for GNO/ME), 0 when only
// the universal-success stub (iigsGsosStub.o) is linked, and 0 when
// no GS/OS surface is linked at all.  Newly-added GS/OS wrappers
// should check this BEFORE issuing a call so the stub can't silently
// fabricate success — see Phase 1.2 of docs/GAP_CLOSURE_PLAN.md.
extern int __gsosAvailable(void);

#ifdef __cplusplus
}
#endif

#endif  // IIGS_GSOS_H
