Arculator podule API v1.0

ask about 32-bit emulators like ArcEm, Red Squirrel, Arculator or RPCEmu here
Post Reply
User avatar
SarahWalker
Posts: 1190
Joined: Fri Jan 14, 2005 3:56 pm
Contact:

Arculator podule API v1.0

Post by SarahWalker » Tue Sep 03, 2019 7:24 pm

I've posted below the podule API currently implemented by the in-progress next version of Arculator, should anyone be interested (I think this is most likely to be flibble and possibly Phlamethrower); it would be nice if this could be used cross-emulator/platform. I think I'm unlikely to change much of the core IO/interrupt/timer APIs but am very open to suggestions on improving the config UI API...

I've got a few podules implemented with this : Acorn AKD52 ST-506, Risc Developments and ideA IDE, a generic ROM podule for HostFS (though I may migrate these modules to 5th column to free up a podule slot), Acorn AKA31 SCSI, MIDI Max, Computer Concepts Lark, HCCS Ultimate CD-ROM, and an in-progress Acorn AKA25 Ethernet I.

Essentially the podule should exist as a DLL/.so/whatever, and should fill out a podule_header_t structure which is returned from a podule_probe() function, and it sort of goes from there.

Code: Select all

#ifndef PODULE_API_H
#define PODULE_API_H

#define PODULE_API_VERSION 1

struct podule_t;

typedef enum podule_io_type
{
        /*Access is in IOC space*/
        PODULE_IO_TYPE_IOC = 0,
        /*Access is in MEMC space*/
        PODULE_IO_TYPE_MEMC,
        /*Access is in EASI space (not Archimedes)*/
        PODULE_IO_TYPE_EASI
} podule_io_type;

typedef struct podule_functions_t
{
        /*init() - Initialise podule instance
          @podule: podule pointer
          
          Returns zero on success, non-zero on error.

          Any instance data should be stored in podule->p*/
        int (*init)(struct podule_t *podule);
        /*close() - Close podule instance
          @podule: podule pointer
          
          This function is responsible for freeing any instance data in podule->p*/
        void (*close)(struct podule_t *podule);

        /*write_b() - Write byte to podule space
          @podule: podule pointer
          @type:   type of access (see @podule_io_type)
          @addr:   address to write to
          @val:    value to write*/
        void (*write_b)(struct podule_t *podule, podule_io_type type, uint32_t addr, uint8_t val);
        /*write_w() - Write word to podule space
          @podule: podule pointer
          @type:   type of access (see @podule_io_type)
          @addr:   address to write to
          @val:    value to write*/
        void (*write_w)(struct podule_t *podule, podule_io_type type, uint32_t addr, uint16_t val);
        /*write_l() - Write long word to podule space
          @podule: podule pointer
          @type:   type of access (see @podule_io_type)
          @addr:   address to write to
          @val:    value to write*/
        void (*write_l)(struct podule_t *podule, podule_io_type type, uint32_t addr, uint32_t val);
        /*read_b() - Read byte from podule space
          @podule: podule pointer
          @type:   type of access (see @podule_io_type)
          @addr:   address to read from

          Returns value read from podule space*/
        uint8_t  (*read_b)(struct podule_t *podule, podule_io_type type, uint32_t addr);
        /*read_w() - Read word from podule space
          @podule: podule pointer
          @type:   type of access (see @podule_io_type)
          @addr:   address to read from

          Returns value read from podule space*/
        uint16_t (*read_w)(struct podule_t *podule, podule_io_type type, uint32_t addr);
        /*read_l() - Read long from podule space
          @podule: podule pointer
          @type:   type of access (see @podule_io_type)
          @addr:   address to read from

          Returns value read from podule space*/
        uint32_t (*read_l)(struct podule_t *podule, podule_io_type type, uint32_t addr);

        /*reset() - Reset podule
          @podule: podule pointer*/
        void (*reset)(struct podule_t *podule);

        /*run() - Callback from podule timer
          @podule:       podule pointer
          @timeslice_us: time expired since last run() call
          
          Returns delay (in us) to reload timer with. Non-zero delay can be used
          to create a free-running timer. Returning zero will stop the timer.
          
          When using a free-running timer, @timeslice_us must be used to check
          how much time has actually passed since the last callback. Emulators
          should support at least a 1ms timer granularity. Arculator will
          generally implement a one instruction granularity*/
        int (*run)(struct podule_t *podule, int timeslice_us);
} podule_functions_t;

#define CONFIG_STRING 0
#define CONFIG_INT 1
#define CONFIG_BINARY 2
#define CONFIG_SELECTION 3
#define CONFIG_SELECTION_STRING 4
#define CONFIG_BUTTON 5

/*Control can not be altered by user*/
#define CONFIG_FLAGS_DISABLED (1 << 0)
/*'name' entry should be prefixed with the prefix passed to config_open()*/
#define CONFIG_FLAGS_NAME_PREFIXED (1 << 1)

typedef struct podule_config_selection_t
{
        /*User-visible description of selection*/
        const char *description;
        /*Value this selection represents. Valid when parent control is
          CONFIG_SELECTION*/
        int value;
        /*Value this selection represents. Valid when parent control is
          CONFIG_SELECTION_STRING*/
        char *value_string;
} podule_config_selection_t;

typedef struct podule_config_item_t
{
        /*Name of config variable. May be NULL if this is a GUI field that isn't
          backed by a config variable. May be prefixed if CONFIG_FLAGS_NAME_PREFIXED
          is set.
          
          If this is non-NULL then any change to the item will cause a config
          update and an emulator reset*/
        const char *name;
        /*User-visible description of item*/
        const char *description;
        /*ID to be used with config_get_current()/config_set_current()*/
        const int id;
        /*Type of control to be presented to the user*/
        const int type;
        /*Combination of CONFIG_FLAGS_* */
        const int flags;
        
        const char *default_string;
        int default_int;
        podule_config_selection_t *selection;
        
        /*function() - Callback function for control
          @window_p: window pointer
          @item:     item pointer
          @new_val:  new value (if valid)
          
          If this item has a type other than CONFIG_BUTTON then this function
          indicates that the user has altered the value of this item, and
          @new_val is valid.

          If the type is CONFIG_BUTTON, then this function indicates that the
          user has clicked on the button, and @new_val is NULL.

          Returns non-zero if this callback has made any changes to other items
          in this dialogue, and that the dialogue needs to be updated.*/
        int (*function)(void *window_p, const struct podule_config_item_t *item, void *new_data);
} podule_config_item_t;

typedef struct podule_config_t
{
        /*Title of dialogue window*/
        const char *title;
        /*init() - function called on config dialogue open
          @window_p: window pointer*/
        void (*init)(void *window_p);
        /*close() - function called if user pressed OK
          @window_p: window pointer
          
          Returns non-zero if the emulated machine needs to be reset*/
        int (*close)(void *window_p);
        
        podule_config_item_t items[];
} podule_config_t;

/*Only one instance of this podule allowed per system*/
#define PODULE_FLAGS_UNIQUE (1 << 0)

typedef struct podule_header_t
{
        /*Podule API version. Should always be set to PODULE_API_VERSION*/
        const uint32_t version;
        /*Podule flags. See PODULE_FLAGS_* */
        const uint32_t flags;
        /*Short internal name, no spaces. Should be <= 15 characters*/
        const char *short_name;
        /*Long user visible name*/
        const char *name;

        const podule_functions_t functions;
        
        podule_config_t *config;
} podule_header_t;

typedef struct podule_t
{
        /*Pointer to header for this podule*/
        const podule_header_t *header;
        /*Pointer to private instance data for this podule*/
        void *p;
} podule_t;

#define CONFIG_FILESEL_LOAD (0)
#define CONFIG_FILESEL_SAVE (1 << 0)

typedef struct podule_callbacks_t
{
        /*set_irq() - Update IRQ state
          @podule: podule pointer
          @state:  new IRQ state*/
        void (*set_irq)(podule_t *podule, int state);
        /*set_fiq() - Update FIQ state
          @podule: podule pointer
          @state:  new FIQ state*/
        void (*set_fiq)(podule_t *podule, int state);

        /*set_timer_delay_us() - Set timer delay until podule run() function called
          @podule:   podule pointer
          @delay_us: desired delay in us*/
        void (*set_timer_delay_us)(podule_t *podule, int delay_us);
        /*get_timer_remaining_us() - Return time until timer expires and run()
                                     is called
          @podule: podule pointer

          Returns: Time until timer expires in us, or zero if timer already
          expired*/
        int (*get_timer_remaining_us)(podule_t *podule);
        /*stop_timer() - Stop timer from running. run() will not be called
          @podule: podule pointer*/
        void (*stop_timer)(podule_t *podule);

        /*config_get_int() - Read integer variable from podule configuration
          @podule: podule pointer
          @name:   name of variable
          @def:    default value to return if variable does not exist
          
          Returns: Value of variable, or @def if variable does not exist*/
        int (*config_get_int)(podule_t *podule, const char *name, int def);
        /*config_get_string() - Read string variable from podule configuration
          @podule: podule pointer
          @name:   name of variable
          @def:    default string to return if variable does not exist

          Returns: Pointer to variable string, or @def if variable does not exist*/
        const char *(*config_get_string)(podule_t *podule, const char *name, const char *def);
        /*config_set_int() - Write integer variable to podule configuration
          @podule: podule pointer
          @name:   name of variable
          @val:    value to write*/
        void (*config_set_int)(podule_t *podule, const char *name, int val);
        /*config_set_string() - Write string variable to podule configuration
          @podule: podule pointer
          @name:   name of variable
          @val:    Pointer to string to write*/
        void (*config_set_string)(podule_t *podule, const char *name, char *val);
        
        /*config_open() - Open new configuration dialogue.
          @window_p: parent window pointer
          @config:   config to open
          @prefix:   prefix for any prefixed config items. May be NULL
          
          Returns: Non-zero if dialogue closed with OK, zero otherwise

          Should only be used from a callback from an already open dialogue*/
        int (*config_open)(void *window_p, podule_config_t *config,
                        const char *prefix);
        /*config_file_selector() - Open a file selector
          @window_p:     parent window pointer
          @title:        window title
          @default_path: Default path for file. May be NULL
          @default_fn:   Default filename for file. May be NULL
          @default_ext:  Default extension for file. May be NULL
          @wildcard:
          @dest:         String to hold destination file path/name
          @dest_len:     Length of @dest, including NULL terminator
          @flags:        Combination of CONFIG_FILESEL_*
          
          Returns: Non-zero if file selected, zero if user dismissed selector
         */
        int (*config_file_selector)(void *window_p, const char *title,
                        const char *default_path, const char *default_fn,
                        const char *default_ext, const char *wildcard,
                        char *dest, int dest_len, int flags);
        /*config_get_current() - Get the value of a config item in the currently
                                 open dialogue
          @window_p: window pointer
          @id:       ID of config item

          Returns: Pointer to value, or NULL if item not found*/
        void *(*config_get_current)(void *window_p, int id);
        /*config_get_current() - Set the value of a config item in the currently
                                 open dialogue
          @window_p: window pointer
          @id:       ID of config item
          @val:      Pointer to new value*/
        void (*config_set_current)(void *window_p, int id, void *val);
} podule_callbacks_t;

/*Main entry point to be implemented by the podule. Podule should store
  callbacks and path locally, and return a pointer to the podule header.

  If the podule can't be initialised (eg due to a missing file), this
  function should return NULL. The podule will then be unavailable to
  the user.*/
const podule_header_t *podule_probe(const podule_callbacks_t *callbacks, char *path);

#endif /*PODULE_API_H*/

cmorley
Posts: 969
Joined: Sat Jul 30, 2016 7:11 pm
Location: Oxford
Contact:

Re: Arculator podule API v1.0

Post by cmorley » Tue Sep 03, 2019 7:48 pm

Will it be able to run ZIDEFS so one can mount a 4 partition disk image as per IanS's 8 &16 bit IDE upgrades?

User avatar
SarahWalker
Posts: 1190
Joined: Fri Jan 14, 2005 3:56 pm
Contact:

Re: Arculator podule API v1.0

Post by SarahWalker » Tue Sep 03, 2019 7:52 pm

If a suitable podule with the correct address map supporting 4 drives is written, yes. That's not really a function of the API though.
Last edited by SarahWalker on Tue Sep 03, 2019 7:53 pm, edited 1 time in total.

hubersn
Posts: 152
Joined: Sun Aug 14, 2016 7:59 pm
Contact:

Re: Arculator podule API v1.0

Post by hubersn » Wed Sep 04, 2019 3:35 pm

SarahWalker wrote:
Tue Sep 03, 2019 7:52 pm
If a suitable podule with the correct address map supporting 4 drives is written, yes. That's not really a function of the API though.
Since you have already implemented the ideA IDE podule, I guess ZIDEFS should "just work"?

hubersn

User avatar
SarahWalker
Posts: 1190
Joined: Fri Jan 14, 2005 3:56 pm
Contact:

Re: Arculator podule API v1.0

Post by SarahWalker » Wed Sep 04, 2019 5:28 pm

Not sure how adaptable ZIDEFS is; ideA (or at least the podule I own) has a somewhat different memory map to the modern IDE podules. As does the RISC Developments podule.
Last edited by SarahWalker on Wed Sep 04, 2019 5:29 pm, edited 1 time in total.

hubersn
Posts: 152
Joined: Sun Aug 14, 2016 7:59 pm
Contact:

Re: Arculator podule API v1.0

Post by hubersn » Wed Sep 04, 2019 9:25 pm

SarahWalker wrote:
Wed Sep 04, 2019 5:28 pm
Not sure how adaptable ZIDEFS is; ideA (or at least the podule I own) has a somewhat different memory map to the modern IDE podules. As does the RISC Developments podule.
Were there multiple different ICS IDE podules? Because ZIDEFS is especially for the ICS IDE podules.

Have fun
hubersn

User avatar
SarahWalker
Posts: 1190
Joined: Fri Jan 14, 2005 3:56 pm
Contact:

Re: Arculator podule API v1.0

Post by SarahWalker » Thu Sep 05, 2019 7:46 pm

Assuming that IanS's interfaces are following what ZIDEFS thinks an ICS interface is, this is the expected memory map (from viewtopic.php?f=16&t=15205) :

Code: Select all

+0x0000 - 0x1FFF ROM window (2 KB)
+0x2000 ROM page register
+0x3000 IDE reg 0
+0x3004 IDE reg 1
+0x3008 IDE reg 2
+0x300C IDE reg 3
+0x3010 IDE reg 4
+0x3014 IDE reg 5
+0x3018 IDE reg 6
+0x301C IDE reg 7
This is the memory map of the ideA board in my 410/1 :

Code: Select all

IOC Address map :
  0000-1fff : ROM
  0000 : ROM page register
  
MEMC address map :
  2000-21ff : IDE registers - bits 6-8 = register index
  2380 : IDE alternate status
This is the memory map of the Risc Developments IDE podule :

Code: Select all

IOC Address map :
  0000-1fff : ROM
  2000 : ROM page register
  2800-281f : IDE registers

User avatar
SarahWalker
Posts: 1190
Joined: Fri Jan 14, 2005 3:56 pm
Contact:

Re: Arculator podule API v1.0

Post by SarahWalker » Thu Sep 05, 2019 7:50 pm

In case there are multiple ICS designs, mine says '(C) BE 1992 IDE I/F V5' on the PCB, and '(C) 1993 ICS Ltd IDE1V5 3V10' on the EPROM. The IDEFS implementation also seems to be somewhat fancier than the normal bog standard one, offering partitioning and password protection among other things.

Post Reply