
//--- implemented IAP functions -----------------------------------------------
// common functions             R1   DPH  DPL  ACC
#define IAP_GET_DEVICE_ID_1     0x00,0x00,0x01,0x00 ///< request device id #1
#define IAP_GET_MANUFACTURER_ID 0x00,0x00,0x00,0x00 ///< request manufacturer id

// for device 89C51 only        R1   DPH  DPL  ACC
<i>[...]</i>

// for device 89V51 only        R1   DPH  DPL  ACC
#define IAP_V51_GET_BOOT_CODE_VERSION 
                                0x00,0x00,0x02,0x00 ///< read id - boot code ver
#define IAP_V51_SET_DOUBLE_CLOCK 
                                0x05,0x00,0x05,0x00 ///< program double clock
#define IAP_V51_SET_ISP_ENTRY_MODE_ALWAYS 
                                0x04,0x00,0x03,0x00 ///< run ISP after reset
#define IAP_V51_SET_ISP_ENTRY_MODE_PIN_LOW(pinl) 
                                0x04,pinl,0x01,0x00 ///< user code if pin high

void init_iap (void)
{
        BSEL = 1; // select bank 0 for reading (89V51 only)
        SWR  = 0; // make bank 1 (boot code) accessible (89V51 only)
        switch (call_iap(IAP_GET_MANUFACTURER_ID)) {

        case 0x15:                                              // Philips
                <i>[...]</i>
                break;

        case 0xBF:                                              // NXP (?)
                switch (call_iap(IAP_GET_DEVICE_ID_1)) {
                case 0x91:                                      // P89V51Rx2xx
                        iap.device = IAP_DEVICE_89V51;
                        if (call_iap(IAP_V51_GET_BOOT_CODE_VERSION) == 0x07)
                                return;
                        upgrade_p89v51_iap();
                        call_iap(IAP_V51_SET_DOUBLE_CLOCK);
                        call_iap(IAP_V51_SET_ISP_ENTRY_MODE_PIN_LOW(0x90 + 0));
                                                        // port P1 + pin 0
                        iap_reboot();
                }
        } // switch

        fatal_error(E_IAP_UNKNOWN_DEVICE_TYPE);
}

/// call IAP routine
/**
 * This function is a wrapper around real IAP routine placed in
 * bootcode area.
 *
 * @warning
 * The function code relies on the fact that current compiler passes
 * function parameters via registers @c R1, @c R2, @c R3, and @c R4,
 * in this order.
 *
 * @return IAP function exit code or value
 */
#pragma location="IAP_SAFE_CODE" // code segment start >= 0x2000
#pragma optimize=no_inline
static char call_iap (const char R1,    ///< 1st parameter passed via R1
                      const char R2,    ///< 2nd parameter passed via R2
                      const char R3,    ///< 3rd parameter passed via R3
                      const char R4)    ///< 4th parameter passed via R4
{
        asm("PUSH 0xA8/*IEN0*/       ");
        asm("CLR  0xA8.7/*EA*/       ");// disable interrupts
        asm("ANL  0xB1/*FCF*/,#~0x01 ");// enable 89V51's bootcode bank
        asm("ANL  ?DPS,#~0x01        ");// select DPTR 0
        asm("MOV  DPH,R2             ");
        asm("MOV  DPL,R3             ");
        asm("MOV  A,R4               ");
        asm("CALL iap_routine        ");// call 89V51's IAP routine (0x1FF0)
        asm("ORL  0xB1/*FCF*/,#0x01  ");// disable 89V51's bootcode bank
        asm("POP  0xA8/*IEN0*/       ");

        return ACC;
}