[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: SLC flaws



* Lutz Donnerhacke wrote:
>          Christian Kahlo
> Bemerkungen zur SLE44/66 Familie
>           1999-11-29

Und weiter geht's.


			      Matthias Brüstle
	Commented disassembly of the CMS/RMS for the Siemens SLE44/66
				 1999-12-01

				  Abstract

	   A plain disassembly read from a chip card is commented.
	   There is also shown a possible attack.


Structure of the CMS/RMS

As is shown in [1] the Chip/Resource Management System (CMS/RMS) is split in
two parts. The first 1kB at the address 0x0000, the second 1kB at the
address 0x4000. In the following I describe only the parts I understood what
they do.

Execution begins at 0x0000. After a normal reset, i.e. with IO pin high it
checks if the card is initialized (0x0047). If it is it jumps to the begin
of the application code at 0x0400. If it is not, it receives an eight byte
key and compares it to the key stored at the last eight bytes of the ROM
(0x43F8). If the key matches it jumps to a command loop in the second halve
of the CMS/RMS (0x403a}). The command loop waits for receiving a byte over
the IO pin. This is used to look up one of 29 function pointers in a table
at 0x4000. This function is then executed. Functions which purpose are
known are shown in table 1.

    Table 1: Known functions executable by the CMS/RMS

      Address | Function
      -----------------------------------------------
      0x00DA  |  Erase and write to EEPROM
      0x02F0  |  Execute application
      0x406F  |  Set special function registers (SFR)
      0x410C  |  Set processor status word (PSW)
      0x41B1  |  Write to RAM
      0x41E4  |  Read from RAM
      0x41F0  |  Read from ROM/EEPROM
      0x4219  |  Execute code in ROM/EEPROM

As written in [1] if the IO pin is low at a reset the first 16 bytes of the
EEPROM are sent through the IO pin. The code executed after this needs
further studying.

Attacks

To get by normal execution of the CPU into the command loop there are two
requirements:

  1. At the start of the EEPROM some bytes have to have specific values.
     (0x8000: 0xA9, 0x8004--6: 0x99, 0x60, 0x19)

  2. The mask key must be known.

In issued cards requirement 1 is not met, because the byte at 0x8000 is
0x33. To fulfill this requirement it could be either this byte modified with
an intrusive physical attack or conditional jumps/calls must be modified by
a power or clock glitch [2]. The second method has to modify instructions at
0x0048 or 0x0053 and at 0x404b.

Requirement 2 is probably easier met because many cards share the same mask
key. To read out the mask key a differential power analysis [3] could be
applied while at key is compared at 0x0319 or the key could be read out by
an intrusive physical attack and used on any other card sharing the same
key. The feasibility of all these attacks is unknown. Requirement 2 can also
be easily met by the issuer of the card.

Conclusions

Never trust anything.

Bibliography

  [1] Kahlo, C.; {Bemerkungen zur SLE44/66 Familie}; Jena; 1999;
      news:slrn8480js.cna.lutz@belenus.iks-jena.de
    
  [2] Anderson, R.; Kuhn, M. G.; {Tamper Resistance - a Cautionary Note},
      In Proceedings of the 2nd. Workshop On Electronic Commerce;
      USENIX Association: Oakland, 1996; pp 1-11;
      http://www.cl.cam.ac.uk/~mgk25/tamper.html
    
  [3] Kocher, P.; Jaffe, J.; Jun, B.; {Differential Power Analysis};
      http://www.cryptography.com/dpa/Dpa.pdf

Appendix A: Commented disassembly of the CMS/RMS

;  Commented 8051 Disassembly of 44C80S Chip Management System
;
;  ADRXH(0b3h):  Contains the hight byte of the address used in
;                EEPROM writes.
;  EETIME(0bdh): Timings for EEPROM writes.
;  EECTRL(0d8h): 
;  EEPAGE(0fbh): 
;  p1.0(090h):   IO pin.
;  094h.0(094h): Direction of IO (0: receive, 1: send).
;  rb1r0(08h):   Used to store 0x43 after the verification of the
;                mask key.
;
        org     0
;
X0000:  clr     a               ; a = 0
        mov     adrxh,#80h      ; [0xB3] = 0x80
        mov     r1,a            ; r1 = 0
        mov     eetime,#31h     ; [0xBD] = 0x31
        mov     0fdh,#0dch      ; [0xFD] = 0xDC
        acall   X03a4           ; Wait dependant on psw.1
        jb      p1.0,X0045      ; if( p1.0 ) init and call ROM application
        ; Send 16 bytes starting at external [0x8000].
        movx    a,@r1           ; \
        mov     r6,a            ; / r6 = external [0x8000]
        mov     r2,#10h         ; r2 = 0x10
        acall   X03a4           ; Wait dependant on psw.1
        sjmp    X0019           ;
X0018:  movx    a,@r1           ; a = external [0x8000...]
X0019:  acall   X03e2           ; Send byte (0x437d).
        inc     r1
        djnz    r2,X0018        ; Loop 16 times
        cjne    r6,#33h,X0030   ; if( ext[0x00] != 0x33 ) jump to 0x0030
        mov     dptr,#X8003     ; dptr = 0x8003
        movx    a,@dptr         ; a = [dptr]
        cpl     a               ; a = ~a;
        mov     r0,#30h         ; r0 = 0x30
        mov     @r0,a           ; [r0] = a;
        acall   X003c
        mov     088h,#44h       ; [0x88] = 0x44 (Probably not TCON)
        acall   X03e2           ; Send byte (0x437d).
X0030:  acall   X03c8           ; Check if p1.6 can be set and cleared.
X0032:  jnc     X0032           ; Wait forever if p1.6 cant be toggled.
        lcall   X4113
        lcall   X411b
X003a:  sjmp    X003a           ; Wait forever
;
X003c:  setb    eectrl.2        ; Set [0xD8].2
        push    b               ; Save b
        mov     b,r2            ; b = r2 (=0)
X0042:  inc     r2              ; r2++
        ajmp    X0133
;
X0045:  acall   X03c8           ; Check if p1.6 can be set and cleared.
        movx    a,@r1           ; a = ext[r1(=0)]
        cjne    a,#33h,X0055    ; if( ext[r1]!=0x33 ) goto X0055
        mov     r1,#4           ; r1 = 4
        movx    a,@r1           ; a = ext[4]
        cjne    a,#0ffh,X003a   ; if( ext[4]!=0xFF ) goto Wait forever
        acall   X00a1           ; ext[0x8003]->[0xBD], ext[0x8007]->[0xFD]
        ajmp    X0400           ; Jump to ROM application.
;
X0055:  inc     sp              ; sp++
        cjne    a,#0a9h,X0069   ; if( a!=0xa9 ) goto X0069
        acall   X02d7           ; Check security area
        acall   X00a1           ; ext[0x8003]->[0xBD], ext[0x8007]->[0xFD]
        acall   X0084           ; receive and copy 8 bytes
X0060:  mov     r2,#40h         ; r2 = 0x40
        mov     a,@r0           ; a = [r0]
X0063:  acall   X02f7
        djnz    r3,X0060        ; Loop r3 (=8?) times
        sjmp    X007f           ; Compare mask key and jump to command loop
;
X0069:  acall   X03c8           ; Check if p1.6 can be set and cleared.
X006b:  jnc     X006b           ; if( p1.6 cant be toggled ) Wait forever
        mov     2fh,#31h        ; [0x2F] = 0x31
X0070:  clr     a               ; a = 0
        lcall   X42e9
        acall   X0084           ; Receive and copy 8 bytes
X0076:  mov     r2,#40h         ; r2 = 0x40
        movx    a,@r0           ; a = ext[r0]
        cpl     a               ; a = ~a
        add     a,@r0           ; a += int[r0]
        acall   X02f7
        djnz    r3,X0076        ; Loop r3 (=8?) times
X007f:  acall   X0319           ; Compare mask key
        ljmp    X403a           ; Command loop
;
; Receive and copy 8 bytes
X0084:  mov     r3,#8           ; r3 = 8
        mov     r1,#30h         ; r1 = 0x30
        acall   X00ce           ; Receive 8 bytes to r1
        mov     r3,#8           ; r3 = 8
X008c:  mov     a,@r0           ; \
        mov     @r1,a           ; ) Copy 8 bytes from [r0] to [r1]
        inc     r0              ; ) (r0 should be here the same as r1)
        inc     r1              ; )
        djnz    r3,X008c        ; /
        mov     r1,#30h         ; r1 = 0x30
        mov     dptr,#X0000     ; dptr = 0x0000
        mov     adrxh,#80h      ; [0xB3] = 0x80
        mov     r3,#8           ; r3 = 8
        mov     b,@r1           ; b = [0x30]
        setb    eectrl.7        ; setb [0xD8].7
        ret     
;
; ext[0x8003]->[0xBD], ext[0x8007]->[0xFD]
X00a1:  mov     r1,#3           ; r1 = 3
        movx    a,@r1           ; a = ext[0x8003]
        mov     eetime,a        ; [0xBD] = ext[0x8003]
        mov     r1,#7           ; r1 = 7
        movx    a,@r1           ; a = ext[0x8007]
        mov     0fdh,a          ; [0xFD] = ext[0x8007]
        ret     
;
; Erase and write 1 byte.
; source: a
; destination: dptr
X00ac:  mov     r7,a            ; r7 = a    \
        mov     a,psw           ; a = psw   ) Copy a to r7 and set r0 to
        anl     a,#18h          ; a &= 0x18 ) the address of r7.
        orl     a,#7            ; a |= 7    )
        mov     r0,a            ; r0 = a    /
        mov     r2,#1           ; r2 = 1
        sjmp    X00f2           ; Erase and write n bytes.
;
; Erase and write 4 bytes.
; source: @r0
; destination: @dptr
X00b8:  mov     r2,#4           ; r2 = 4
        anl     dpl,#0fch       ; dpl &= 0xFC (mask out lower two bits)
        sjmp    X00f2           ; Erase and write n bytes.
;
; Erase and write n bytes somehow.
; source: @r0
; destination: @dptr
; length: r2
X00bf:  mov     a,#0c4h         ; a = 0xC4 (Sets flags for write procedure)
        sjmp    X00f3           ; Erase and write n bytes.
;
; Erase and write n bytes somehow.
; source: @r0
; destination: @dptr
; length: r2
X00c3:  mov     a,#40h          ; a = 0x40 (Sets flags for write procedure)
        sjmp    X00f3           ; Erase and write n bytes.
;
; Read dptr and a bytes.
; Returns r0 with the address of received data and r0 with the number
; of bytes received.
X00c7:  mov     r3,a            ; r3 = a
        mov     b,a             ; b = a
        acall   X02a8           ; Read 3 bytes, set dptr and return last byte.
        sjmp    X00d1           ; Read a-1 bytes to r1
;
; Receive r3/b bytes.
; Returns r0 with the address of received data and r0 with the number
; of bytes received.
X00ce:  lcall   X43b3           ; Receive byte
X00d1:  mov     @r1,a           ; [r1] = a
        inc     r1              ; r1++
        djnz    r3,X00ce        ; Loop r3 times
        mov     r2,b            ; r2 = b
        mov     r0,#30h         ; r0 = 0x30
X00d9:  ret     
;
; Erase and write data in EEPROM.
; (Called from fptr table at 0x4000.)
X00da:  acall   X03e5           ; Check if key checked and receive byte.
        acall   X00c7           ; Receive dptr and byte1 bytes
        mov     a,dph           ; a = dph
        cjne    a,#80h,X00ed    ; if( dph!=0x80 ) goto X00ed
        mov     a,dpl           ; a = dpl
        jnz     X00ed           ; if( dpl!=0 ) foto X00ed
        mov     37h,0fdh        ; [0x37] = [0xFD] \ Only when dptr==0x8000
        mov     38h,#72h        ; [0x38] = 0x72   /
X00ed:  mov     r1,rb1r0        ; r1 = 0x43 (from dph)
X00ef:  cjne    r1,#43h,X00ef   ; if( r1!=0x43 ) wait forever (key not checked)
; Erase and write n bytes.
; source: @r0
; destination: @dptr
; length: r2
X00f2:  clr     a               ; a = 0
X00f3:  xch     a,r2            ; swap a and r2
        jz      X00d9           ; if( a==0 ) ret (no data received)
        xch     a,r2            ; restore a and r2
        setb    eectrl.2        ; Set [0xD8].2
        push    b               ; Save b (data length?)
        mov     b,a             ; b = a
        mov     adrxh,#80h      ; [0xB3] = 0x80
        mov     r1,#0           ; r1 = 0
        movx    a,@r1           ; a = ext[r1(=0)]
        cjne    a,#33h,X0123    ; if( a!=0x33 ) goto 0x0123
        mov     r1,#7           ; r1 = 7
        movx    a,@r1           ; a = ext[r1(=7)]
        mov     0fdh,a          ; [0xFD] = ext[r1(=7)]
        mov     a,dph           ; a = dph
        cjne    a,#80h,X012a    ; if( dph!=0x80 ) goto 0x12a
        mov     a,dpl           ; a = dpl
        cjne    a,#20h,X0115    ; \
X0115:  jnc     X0133           ; / if( dpl>=0x20 ) goto 0x0133
        jnb     acc.4,X0121     ; if( !acc.4 ) goto X0223
        nop
        nop
        nop
        setb    b.2             ; set b.2
        sjmp    X0135           ; goto X0135
X0121:  ajmp    X0223           ; goto X0223
X0123:  mov     r1,rb1r0        ; r1 = 0x43 (from dph)
X0125:  cjne    r1,#43h,X0125   ; if( r1!=0x43) loop forever (no key checked)
        sjmp    X0133           ; goto X0133
X012a:  jc      X0121           ; if( c ) goto X0223 (dph<0x80)
        subb    a,#80h          ; a -= 0x80
        cjne    a,0fah,X0131    ; \
X0131:  jnc     X0121           ; / if( a>=[0xFA] ) goto X0223
X0133:  mov     c,b.6           ; c = b.6
X0135:  orl     eectrl,#81h     ; [0xD8] |= 0x81
        mov     a,r0            ; a = r0
        mov     r1,a            ; r1 = r0
        mov     a,r2            ; a = r2
        mov     r3,a            ; r3 = r2
X013c:  movx    a,@dptr         ; a = ext[dptr]
        xrl     a,@r1           ; \
        anl     a,@r1           ; / a = (a ^ [r1]) & [r1]
        addc    a,#0ffh         ; a += 0xFF + c (c set if at least once a!=0)
        inc     dptr            ; dptr++
        inc     r1              ; r1++
        djnz    r3,X013c        ; Loop r3 times
        clr     a               ; a = 0
        mov     acc.5,c         ; acc.5 = c
        anl     c,/b.2          ; c &= !b.2
        mov     b.2,c           ; b.2 = c
        add     a,#20h          ; a = 0x20
        xch     a,dpl           ; swap a and dpl
        subb    a,r2            ; dpl -= r2
        xch     a,dpl           ; swap a and dpl
        jnc     X0157           ; \
        dec     dph             ; / dph -= c
X0157:  xrl     a,#60h          ; a ^= 0x60
; Write n bytes?
X0159:  xch     a,r0            ; \
        mov     r1,a            ; ) r1 = r0
        xch     a,r0            ; /
X015c:  jbc     eectrl.3,X015c  ; if( [0xD8].3 ) loop forever
        xch     a,eectrl        ; swap a and [0xD8]
        jnb     acc.2,X0121     ; if( !acc.2 ) goto X0223
        mov     a,eepage        ; a = [0xFB]
        cpl     a               ; a = ~a
        inc     a               ; a++
        orl     a,dpl           ; a |= dpl
        cpl     a               ; a = ~a
        mov     r3,a            ; r3 = a
        inc     r3              ; r3++
        cpl     a               ; a = ~a
        add     a,r2            ; a += r2
        jc      X0174           ; if( c ) goto X0174
        mov     a,r2            ; \
        mov     r3,a            ; / r3 = r2
        clr     a               ; a = 0
X0174:  mov     r2,a            ; r2 = a
        jb      b.1,X0180       ; if( b.1 ) goto X0180
X0178:  mov     a,@r1           ; a = [r1]
        movx    @dptr,a         ; ext[dptr] = a
        inc     r1              ; r1++
        inc     dptr            ; dptr++
        djnz    r3,X0178        ; Loop r3 times
        sjmp    X018a           ; goto X018a
X0180:  clr     c               ; Clear c
X0181:  mov     a,@r1           ; a = [r1]
        jc      X0185           ; if( c (every second interation) ) goto X0185
        movx    @dptr,a         ; ext[dptr] = a
X0185:  cpl     c               ; Toggle c
        inc     r1              ; r1++
        inc     dptr            ; dptr++
        djnz    r3,X0181        ; Loop r3 times
X018a:  jnb     eectrl.4,X0199  ; if( ![0xD8].4 ) goto X0199
        jnb     b.3,X0199       ; if( !b.3 ) goto X0199
        clr     a               ; a = 0
        xch     a,r1            ; swap a and r1
        mov     r3,a            ; r3 = a
        mov     adrxh,#80h      ; [0xB3] = 0x80
        movx    a,@r1           ; a = ext[r1]
        xch     a,r3            ; swap a and r3
        mov     r1,a            ; r1 = a (=restore r1)
X0199:  setb    eectrl.0        ; Set [0xD8].0
        mov     a,eetime        ; a = [0xBD]
        rrc     a               ; Rotate right a into c
        rrc     a               ; Rotate right a into c
        anl     a,#3fh          ; a &= 0x3F
        addc    a,#0fch         ; a += 0x0F + c
        jc      X01a6           ; if( c ) goto X01a6
        clr     a               ; a = 0
X01a6:  inc     a               ; a++
        mov     adrxh,a         ; [0xB3] = a
        setb    eectrl.1        ; Set [0xD8].1
        mov     a,0fdh          ; a = [0xFD]
        jnb     eectrl.6,X01b1  ; if( ![0xD8].6 ) goto X01b1
        swap    a               ; swap nibbles of a
X01b1:  add     a,#0fdh         ; a += 0xFD
        anl     a,#0fh          ; a &= 0x0F
        jz      X01c0           ; if( a==0 ) goto X01c0
X01b7:  dec     a               ; a--
        mov     r3,adrxh        ; r3 = [0xB3]
X01ba:  djnz    r3,X01ba        ; Wait dependant on r3
        inc     eectrl          ; [0xD8]++
        jnz     X01b7           ; if( a!=0 ) goto X01cb
X01c0:  jb      b.4,X01cb       ; if( b.4 ) goto X01cb
        mov     a,eetime        ; a = [0xBD]
        jb      eectrl.6,X01ca  ; if( [0xD8].6 ) goto X01ca
        clr     c               ; Clear c
        rrc     a               ; Rotate right a into c
X01ca:  dec     a               ; a--
X01cb:  mov     r3,#20h         ; r3 = 0x20
X01cd:  djnz    r3,X01cd        ; Wait dependant on r3
        jnz     X01ca           ; if( a!=0 ) goto X01ca
        mov     a,#84h          ; a = 0x84
        xch     a,eectrl        ; swap a with [0xD8]
        anl     a,#0f0h         ; a &= 0xF0
        jz      X01e4           ; if( a==0 ) goto X01e4
        jb      acc.4,X01e4     ; if( acc.4 ) goto X01e4
        xch     a,r2            ; \
        jz      X01e5           ; ) if( r2==0 ) goto X01e5
        xch     a,r2            ; /
        ajmp    X015c           ; goto X015c
X01e2:  ajmp    X0157           ; goto X0157
X01e4:  mov     r2,a            ; r2 = a
X01e5:  mov     a,r0            ; a = r0
        xch     a,r1            ; swap a and r1
        clr     c               ; Clear c
        subb    a,r0            ; a -= r0
        mov     r3,a            ; r3 = a
        xch     a,r2            ; swap a and r2
X01eb:  jbc     eectrl.3,X01eb  ; if( [0xD8].3 ) clear [0xD8].3, goto X01eb
        xch     a,dpl           ; \
        clr     c               ; ) dpl -= r2
        subb    a,r2            ; )
        xch     a,dpl           ; /
        jnc     X01f8           ; \
        dec     dph             ; / dph -= c
X01f8:  jbc     b.2,X01e2       ; if( b.2 ) clear b.2, goto X01e2
        jb      eectrl.0,X021c  ; if( [0xD8].0 ) goto X021c
        jnb     acc.4,X020a     ; if( !acc.4 ) goto X020a
        mov     eectrl,#50h     ; [0xD8] = 0x50
        movx    @dptr,a         ; ext[dptr] = a
        setb    eectrl.0        ; set [0xD8].0
        mov     eectrl,#84h     ; [0xD8] = 0x84
X020a:  push    dpl             ; save dpl
X020c:  movx    a,@dptr         ; a = ext[dptr]
        inc     dptr            ; dptr++
        djnz    r3,X020c        ; Loop r3 times
        clr     eectrl.4        ; clear [0xD8].4
        setb    eectrl.1        ; Set [0xD8].1
        pop     dpl             ; Restore dpl
        jnc     X0223           ; if( !c (from inc dptr) ) goto X0223
        dec     dph             ; dph--
        sjmp    X0223           ; goto X0223
X021c:  mov     a,eetime        ; a = [0xBD]
        clr     c               ; Clear c
        rrc     a               ; Rotate right a into c
        mov     r3,a            ; r3 = a
X0221:  djnz    r3,X0221        ; Wait dependant on r3
X0223:  mov     a,#55h          ; a = 0x55
        jb      b.5,X0247       ; if( b.5 ) goto X0247
        jb      b.7,X0235       ; if( b.7 ) goto X0235
X022b:  movx    a,@dptr         ; a = ext[dptr]
        xrl     a,@r1           ; a ^= [r1]
        jnz     X0244           ; if( [r1]!=ext[dptr] ) goto X0244
        inc     r1              ; r1++
        inc     dptr            ; dptr++
        djnz    r2,X022b        ; Loop r2 times
        sjmp    X023d           ; goto X023d
X0235:  movx    a,@dptr         ; a = ext[dptr]
        cpl     a               ; a = ~a
        jnz     X0244           ; if( a!=0 ) goto X0244
        inc     r1              ; r1++
        inc     dptr            ; dptr++
        djnz    r2,X0235        ; Loop r2 times
X023d:  cpl     eectrl.0        ; Toggle [0xD8].0
        clr     eectrl.1        ; Clear [0xD8].1
        jb      eectrl.0,X01e5  ; if( [0xD8].0 ) goto X01e5
X0244:  xch     a,r1            ; \
        mov     r0,a            ; ) swap r0 and r1
        mov     a,r1            ; /
X0247:  mov     eectrl,#0       ; [0xD8] = 0
        pop     b               ; Restore b
        ret     
;
; (Called from fptr table at 0x4000.)
X024d:  acall   X02a6           ; Read 4 B, set dptr, return last Byte
X024f:  mov     r2,eepage       ; r2 = [0xFB]
X0251:  xch     a,r2            ; \
        mov     r3,a            ; ) r3 = r2
        xch     a,r2            ; /
X0254:  mov     @r1,a           ; \
        inc     r1              ; ) copy a r3 times to [r1++]
        djnz    r3,X0254        ; /
X0258:  setb    eectrl.2        ; Set [0xDB].2
        push    b               ; Save b
        mov     b,#20h          ; b = 0x20
        mov     c,2fh.7         ; c = Saved [0xD8].7 (->X02d0)
        mov     b.2,c           ; b.2 |= c
        mov     adrxh,#80h      ; \
        mov     r0,#0           ; ) a = xternal [0x8000]
        movx    a,@r0           ; /
        cjne    a,#0a9h,X0299
        setb    b.3             ; Set b.3
X026e:  mov     r0,#30h
        mov     a,2fh
        jnc     X027a
        cjne    a,#31h,X027a
        jbc     acc.0,X0297
X027a:  anl     a,#70h
        cjne    a,#70h,X0283
        clr     acc.5
        setb    b.4
X0283:  jnb     2fh.3,X0288
        setb    acc.7
X0288:  jnb     2fh.2,X028d
        setb    b.1
X028d:  jnb     2fh.1,X0292
        clr     b.5
X0292:  mov     r1,rb1r0        ; r1 = 0x43 (from dph)
X0294:  cjne    r1,#43h,X0294   ; Loop forever if master key was not checked.
X0297:  ajmp    X0159           ; Write n bytes?
X0299:  acall   X03c8           ; Check if p1.6 can be set and cleared.
        jc      X026e
        sjmp    X0247
;
; (Called from fptr table at 0x4000.)
X029f:  acall   X02a6           ; Read 4 B, set dptr, return last Byte
        mov     r2,a            ; r2 = a
        acall   X03e5           ; Check if key checked and receive byte.
        sjmp    X0251
;
; Read 4 bytes from IO, set dptr and return last byte.
X02a6:  acall   X02b7           ; Read byte and do magic with [0xD8].
X02a8:  mov     r1,#30h         ; r1 = 0x30
        acall   X02ae           ; Read dptr from IO.
        ajmp    X03e5           ; Check if key checked and receive byte.
;
; Read dptr from IO.
X02ae:  acall   X03e5           ; Check if key checked and receive byte.
        mov     dph,a           ; dph = a
        acall   X03e5           ; Check if key checked and receive byte.
        mov     dpl,a           ; dpl = a
        ret     
;
; Read byte and do magic with [0xD8].
X02b7:  acall   X03e5           ; Check if key checked and receive byte.
X02b9:  rl      a               ; \
        cjne    a,#20h,X02bd    ; ) (a&0x7F) < 0x10 ?
X02bd:  rr      a               ; /
        jnc     X02d0           ; jump if above is false
        mov     eectrl,#84h     ; [0xD8] = 0x84
        anl     a,#83h          ; a &= 0x83
        xch     a,eectrl        ; swap a, [0xD8]
        jb      acc.2,X02cf     ; if( [0xD8].2 ) jump to ret
X02ca:  mov     eectrl,#80h     ; [0xD8] = 0x80
        clr     eectrl.7        ; [0xD8].7 = 0
X02cf:  ret     
X02d0:  mov     2fh,a           ; Copy a to bit addressable space.
        ret     
;
; Loop, if p1.6 can be set and cleared. Return in a.
; (Called from fptr table at 0x4000.)
X02d3:  acall   X03c8           ; Check if p1.6 can be set and cleared.
        rlc     a
        ret     
;
; Checks if security area is initalized.
X02d7:  mov     dptr,#X8000     ; Load DPTR with start address of security area.
        movx    a,@dptr
X02db:  cjne    a,#0a9h,X02db   ; Loop forever if 0x8000 is not 0xA9.
        mov     dptr,#X8004     ;   (Code byte)
        movx    a,@dptr
X02e2:  cjne    a,#99h,X02e2    ; Loop forever if 0x8004 is not 0x99.
        inc     dptr            ;   (Internal controll byte)
        movx    a,@dptr
X02e7:  cjne    a,#60h,X02e7    ; Loop forever if 0x8005 is not 0x60.
        inc     dptr            ;   (Internal controll byte)
        movx    a,@dptr
X02ec:  cjne    a,#19h,X02ec    ; Loop forever if 0x8006 is not 0x19.
        ret                     ;   (Internal controll byte)
;
; Call ROM application.
; (Called from the fptr table at 0x4000.)
X02f0:  setb    p1.0            ; Set IO high.
        mov     94h,#40h        ; Set 94h.6
        ajmp    X0400           ; Jump to ROM application.
;
X02f7:  mov     @r0,#0ffh       ; [r0] = 0xFF
        xch     a,r3            ; \
        jnb     acc.0,X02ff     ; ) if( r3.0 ) [0xb3]++
        inc     adrxh           ; )
X02ff:  xch     a,r3            ; /
        inc     r0              ; r0++
X0301:  mul     ab              ; ba = a*b
        xrl     a,@r1           ; \
        mov     @r1,a           ; ) [r1++] ^= a
        inc     r1              ; /
        cjne    r1,#38h,X030a   ; \ if( r1==0x38 ) r1=0x30
        mov     r1,#30h         ; /
X030a:  mov     a,b             ; a = b
        mov     b,@r1           ; b = [r1]
        xrl     a,@r1           ; a ^= [r1]
        mov     @r1,a           ; [r1] = a ([r1]^=b)
        jnb     acc.5,X0315     ; \ if( acc.5 ) {
        inc     dptr            ; )   dptr++; a=0;
        clr     a               ; / }
X0315:  movc    a,@a+dptr       ; a = [dptr+a]
        djnz    r2,X0301        ; Loop r2 times
        ret     
;
; Compare mask key
X0319:  mov     dptr,#X43f8     ; Setup mask key pointer
        mov     rb1r0,dph       ; Save dph to r0 at register bank 1.
        clr     eectrl.7        ; Clear bit D8h.7
        mov     r2,#8
X0323:  clr     a
        movc    a,@a+dptr       ; \ a = [dptr++]
        inc     dptr            ; / [dptr]=21 c0 35 08 81 db d5 08
        xrl     a,@r1
        inc     r1
        addc    a,#0ffh
        xrl     rb0r3,a         ; r3@rb0 ^= a (r3@rb0 should be 0 at the end)
        djnz    r2,X0323        ; Loop 8 times
        cpl     c               ; c is now set only if all 8 bytes matched
        jbc     cy,X0335        ; Jump if c, c=0
X0332:  ljmp    X0000           ; Start if not all 8 bytes matched
X0335:  jc      X0332           ; Protection to get over jbc by a power glitch?
        mov     a,r3
        jnz     X0332           ; Start if r3!=0
        inc     a
        jz      X0332           ; Start if r3==0xFF
        ret     
;
; Receive byte through p1.0 and write it into a and set c if there
; was an error.
; Call at X034a: 9600bps@3.57MHz
; Call at X0348: 9600bps@4.91MHz
X033e:  jb      p1.0,X033e      ; Wait for p1.0==0. (Startbit)
        acall   X03c6           ; NOP function
        acall   X03c6           ; NOP function
        nop     
        ajmp    X034a
X0348:  setb    psw.1           ; Set psw.1
X034a:  clr     f0              ; Clear f0
        mov     r0,#9
        acall   X038a           ; Read 2 out of 3 of p1.0 (Startbit)
        jnc     X0354           ; \
        setb    f0              ; / if( c ) f0=1;
X0354:  rrc     a               ; Rotate right a into carry, i.e. carry into a
        acall   X03ac           ; Wait dependant on psw.1
        acall   X038a           ; Read 2 out of 3 of p1.0 (Byte and Parity)
        djnz    r0,X0354        ; Loop 9 times
        jc      X0361           ; \
        orl     c,p             ; ) Set c if there was a parity error.
        sjmp    X0365           ; )
X0361:  anl     c,/p            ; /
        nop                     ; Delay
        nop                     ; Delay
X0365:  orl     c,f0            ; c = c | f0
        anl     psw,#0ddh       ; Clear f0 and psw.1
        ret     
;
; Send byte a including startbit and parity through p1.0
; Call at X036d: 9600bps@4.91MHz
; Call at X036b: 9600bps@3.57MHz
X036b:  setb    psw.1           ; Set psw.1
X036d:  orl     94h,#1          ; Set 94h.0
        mov     r0,#9
        mov     c,p             ; Set carry to the parity of a
        clr     p1.0            ; Clear p1.0 (Startbit)
        inc     r7              ; NOP?
        dec     r7              ; NOP?
X0378:  acall   X03a1           ; Wait dependant on psw.1
        rrc     a               ; Rotate a right into carry
        mov     p1.0,c          ; Write c to p1.0 (Byte and Parity)
        djnz    r0,X0378        ; Loop 9 times
        acall   X03a1           ; Wait dependant on psw.1
        swap    a               ; Swap nibbles in a
        setb    p1.0            ; Set p1.0 (Stopbits?)
        anl     94h,#0feh       ; Clear 94h.0
        clr     psw.1           ; Clear psw.1
        ret     
;
; 2 out of 3 of p1.0
X038a:  mov     r7,#3
        mov     r2,a            ; Save a
        clr     a
X038e:  mov     c,p1.0          ; Read p1.0
        addc    a,#0
        jnb     psw.1,X0399     ; Delay dependant on psw.1
        acall   X03c6           ; NOP function
        sjmp    X039b
X0399:  xch     a,r0            ; NOP?
        xch     a,r0            ; NOP?
X039b:  djnz    r7,X038e        ; 3 Loops
        mov     c,acc.1         ; Set carry if at least 2
        mov     a,r2            ; Restore a
        ret     
;
; Wait three times according to psw.1.
X03a1:  jnb     psw.1,X03a8     ; \
X03a4:  mov     r7,#1ah         ; ) if( psw.1 ) r7=0x1A;
        sjmp    X03aa           ; ) else r7=0x13;
X03a8:  mov     r7,#13h         ; /
X03aa:  djnz    r7,X03aa        ; Waitloop.
X03ac:  jnb     psw.1,X03b3     ; \
        mov     r7,#3           ; ) if( psw.1 ) r7=0x03;
        sjmp    X03b5           ; ) else r7=0x04;
X03b3:  mov     r7,#4           ; /
X03b5:  djnz    r7,X03b5        ; Waitloop.
        jnb     psw.1,X03c2     ; \
        mov     r7,#0eh         ; ) if( psw.1 ) r7=0x0E;
        sjmp    X03c4           ; ) else r7=0x05;
        mov     r7,#3           ; \ Dead code
        sjmp    X03c4           ; /
X03c2:  mov     r7,#6           ; /
X03c4:  djnz    r7,X03c4        ; Waitloop.
; NOP function
X03c6:  nop     
        ret     
;
; Look, if p1.6 can be set and cleared. Returned in c.
X03c8:  orl     94h,#40h
        setb    p1.6
        mov     c,p1.6
        clr     p1.6
        anl     c,/p1.6
        ret     
;
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
;
; (Called from fptr table at 0x4000.)
X03dd:  acall   X02a8
        mov     r2,a
        ajmp    X00bf
;
X03e2:  ljmp    X437d           ; Send byte (0x437d).
X03e5:  ljmp    X43ae           ; Check if key checked and receive byte.
;
; Jump table
X03e8:  ret
        nop
X03ea:  ret
        nop
X03ec:  ret
        nop
X03ee:  ajmp    X00c3           ; Erase and Write ???
X03f0:  ajmp    X00f2           ; Erase and Write n bytes.
X03f2:  ajmp    X033e           ; Wait for byte and write it into a.
X03f4:  ajmp    X00b8           ; Erase and Write 4 bytes. (obsolete)
X03f6:  ajmp    X00ac           ; Erase and Write 1 byte. (obsolte)
X03f8:  ajmp    X034a           ; Receive into a without setting psw.1.
                                ;   (9600bps@3.57MHz)
X03fa:  ajmp    X0348           ; Receive into a with setting psw.1.
                                ;   (9600bps@4.91MHz)
X03fc:  ajmp    X036d           ; Transmit a without setting psw.1.
                                ;   (9600bps@3.57MHz)
X03fe:  ajmp    X036b           ; Transmit a with setting psw.1.
                                ;   (9600bps@4.91MHz)

----- 15 kB USER MASK

; Function pointer table with 29 pointers.
X4000:  42 59 40 65 41 0c 02 f0
X4008:  40 60 03 dd 00 da 02 4d
X4010:  02 9f 43 00 41 69 42 8e
X4018:  42 ae 40 f3 42 db 42 1f
X4020:  41 1b 41 13 41 20 41 7f
X4028:  02 d3 41 b1 41 e4 41 f0
X4030:  42 19 40 6f 40 a6 43 32
X4038:  41 f0
;
; Command loop
X403a:  acall   X437d           ; Send byte (0x437d).
        acall   X43d2           ; NOP
        acall   X43ae           ; Check if key checked and receive byte.
        mov     r4,a            ; r4=a
        cjne    a,#1dh,X4044    ; \ if( a>=0x1D ) goto 0x403a
X4044:  jnc     X403a           ; / (Checks for ptr table boundaries.)
        lcall   X03c8           ; Check if p1.6 can be set and cleared.
        jc      X404e
        lcall   X02d7           ; Check security area.
X404e:  mov     a,r4            ; \ fptr loopup.
        mov     dptr,#X4000     ; )
        rl      a               ; )
        movc    a,@a+dptr       ; )
        mov     r0,a            ; )
        mov     a,r4            ; )
        rl      a               ; )
        inc     a               ; )
        movc    a,@a+dptr       ; )
        mov     dph,r0          ; /
        acall   X405f           ; Jump to function looked up.
        sjmp    X403a           ; Jump to start of waiting loop.
;
X405f:  jmp     @a+dptr         ; Jump to function looked up.
;
; Send value of port 1 and toggle p1.7.
; (Called from fptr table at 0x4000.)
X4060:  mov     a,p1            ; a=p1
        cpl     p1.7            ; p1.7 = ~p1.7
        ret     
;
; Set [0x88].
; (Called from fptr table at 0x4000.)
X4065:  acall   X43ae           ; Check if key checked and receive byte.
        setb    p1.0            ; Set p1.0
X4069:  mov     94h,#40h        ; [0x94] = 0x40
        mov     088h,a          ; [0x88] = a
        ret     
;
; Verify again mask key and set [0xBD], [0xD8] or [0xFD].
; (Called from fptr table at 0x4000.)
X406f:  acall   X4085           ; Verify mask key for a second time.
        acall   X43ae           ; Check if key checked and receive byte.
        mov     r3,a            ; r3 = a
        acall   X43ae           ; Check if key checked and receive byte.
        jz      X407e           ; if( byte2==0 ) goto X407e
        dec     a               ; a--
        jz      X4081           ; if( byte2==1 ) goto X4081
        mov     0fdh,r3         ; [0xFD] = byte1
        ret     
        ; For byte2==0
X407e:  mov     eetime,r3       ; [0xBD] = byte1
        ret     
        ; For byte2==1
X4081:  mov     a,r3            ; a = byte1
X4082:  ljmp    X02b9           ; Do magic with [0xD8].
        ; Verify again mask key.
X4085:  push    dph             ; \
        push    dpl             ; / Save dptr
        mov     dptr,#X43f8     ; dptr = 0x43f8 (address of mask key)
        mov     r3,#8
        clr     c               ; Clear c
X408f:  push    psw             ; Save psw
        acall   X43ae           ; Check if key checked and receive byte.
        mov     r0,a            ; r0 = a
        pop     psw             ; Restore psw
        clr     a               ; a = 0
        movc    a,@a+dptr       ; a = [dptr]
        xrl     a,r0            ; a ^= r0
        addc    a,#0ffh         ; a += 0xFF + c
        inc     dptr            ; dptr++
        djnz    r3,X408f        ; Loop 8 times
X409e:  jc      X409e           ; Loop forever if( c i.e. key did not match )
        pop     dpl             ; \
        pop     dph             ; / Restore dptr
        clr     a               ; a = 0
        ret     
;
; (Called from fptr table at 0x4000.)
X40a6:  acall   X40cd
        acall   X40ec           ; NOP? It just does the last instructions again
        mov     r3,a            ; r3 = 0 \
        mov     r4,a            ; r4 = 0 ) Counter
        mov     r5,a            ; r5 = 0 /
X40ad:  movx    a,@dptr         ; a = [dptr] (= [0x80??])
        ; Count bits differing between byte2 and [dptr]
        xrl     a,r6            ; a ^= byte2
        jz      X40c1           ; if( byte2==[dptr] ) goto X40c1
        mov     r7,#8
X40b3:  rrc     a               ; Rotate right a into c
        jnc     X40bf           ; if( !c ) goto X40bf
        inc     r5              ; r5++
        cjne    r5,#0,X40bf     ; if( r5 ) goto X40bf
        inc     r4              ; r4++
        cjne    r4,#0,X40bf     ; if( r4 ) goto X40bf
        inc     r3              ; r3++
X40bf:  djnz    r7,X40b3        ; Loop 8 times
X40c1:  inc     dptr            ; dptr++
        djnz    r1,X40ad        ; Loop 0xE0/0xC0 times
        djnz    r2,X40ad        ; Loop [0xFA] times
        lcall   X02ca           ; Do magic with [0xD8].
        mov     r2,#3           ; r2 = 3
        ajmp    X415d
;
X40cd:  acall   X43ae           ; Check if key checked and receive byte.
        mov     r5,a            ; r5 = byte1
        acall   X43ae           ; Check if key checked and receive byte.
        mov     r6,a            ; r6 = byte2
X40d3:  mov     dptr,#X8000     ; dptr = 0x8000
        movx    a,@dptr         ; a = [0x8000]
        mov     r2,a            ; r2 = [0x8000]
        mov     a,r6            ; a = byte2
        xch     a,r5            ; swap a, r5
        acall   X4082           ; Do magic with [0xD8].
        mov     a,r2            ; a = [0x8000]
        mov     r1,#0e0h        ; r1 = 0xe0
        mov     r2,0fah         ; r2 = [0xFA]
        xrl     a,#0a9h         ; a ^= 0xA9
        jnz     X40f2           ; if( [0x8000]!= 0xA9 ) return
        cjne    r2,#40h,X40e8   ; \
X40e8:  jc      X40ec           ; / if( [0xFA]<0x40 ) goto X40ec
        mov     r1,#0c0h        ; r1 = 0xC0
X40ec:  mov     a,r1            ; a = r1
        dec     a               ; a--
        cpl     a               ; a = ~a
        mov     dpl,a           ; dpl = a
        clr     a               ; a = 0
X40f2:  ret     
;
; (Called from fptr table at 0x4000.)
X40f3:  lcall   X02b7           ; Read byte and do magic with [0xD8].
        acall   X43ae           ; Check if key checked and receive byte.
        mov     r6,a            ; r6 = byte1
        acall   X43ae           ; Check if key checked and receive byte.
        mov     r5,a            ; r5 = byte2
        jz      X40ff           ; if( byte1==0 ) goto X40ff
        inc     r6              ; byte1++
X40ff:  acall   X43ae           ; Check if key checked and receive byte.
        lcall   X00c7
X4104:  lcall   X0258
        djnz    r5,X4104        ; if( r5 ) goto X4104
        djnz    r6,X4104        ; if( r6 ) goto X4104
        ret     
;
; Set PSW and return 0x72.
; (Called from fptr table at 0x4000.)
X410c:  acall   X43ae           ; Check if key checked and receive byte.
        mov     psw,a           ; psw = byte1
        mov     a,#72h          ; a = 0x72
        ret     
;
; (Called also from fptr table at 0x4000.)
X4113:  clr     c               ; Clear c
        mov     r7,0fah         ; r7 = [0xFA]
        mov     dptr,#X8010     ; dptr = 0x8010
        sjmp    X4126
;
; (Called also from fptr table at 0x4000.)
X411b:  clr     c               ; Clear c
        mov     r7,pwena        ; r7 = [0xFE]
        sjmp    X4123
;
; (Called from fptr table at 0x4000.)
X4120:  setb    c               ; Set c
        mov     r7,#0ah         ; r7 = 0x0A
X4123:  mov     dptr,#X0000     ; dptr = 0x0000
X4126:  clr     a               ; a = 0
        mov     r0,a            ; r0 = 0
        mov     r3,a            ; r3 = 0 \
        mov     r4,a            ; r4 = 0 / Counter
        mov     r5,a            ; r5 = 0
        mov     r6,#5ah         ; r6 = 0x5A
        mov     b,r6            ; b = 0x5A
        jnc     X414c           ; if( !c ) jump to 414c
X4131:  mov     a,#0ffh         ; a = 0xFF
        db      0a5h            ; Reserved instruction
        nop     
        xch     a,r6            ; \
        xrl     a,r6            ; ) r6 = (r6 ^ a) >>> 1
        rr      a               ; )
        xch     a,r6            ; /
        add     a,r5            ; a += r5
        mov     r5,a            ; r5 = a
        jnc     X4142           ; if( (a+r5)<=0xFF ) goto X4142
        inc     r4              ; r4++
        mov     a,r4            ; a = r4
        jnz     X4142           ; if( r4!=0 ) goto X4142
X4141:  inc     r3              ; r3++
X4142:  inc     dptr            ; dptr++
        djnz    r0,X4131        ; Loop r0 (=0?) times
        djnz    r7,X4131        ; Loop 10 times
        sjmp    X415b
;
X4149:  clr     a               ; a = 0
        mov     dpl,a           ; dpl = 0
X414c:  db      0a5h            ; Reserved instruction
        nop     
        add     a,r5            ; \
        mov     r5,a            ; / r5 += a
        mov     a,dpl           ; \
        addc    a,r4            ; ) r4 += dpl + c
        mov     r4,a            ; /
        jnc     X4157           ; if( !c ) goto X4157
        inc     r3              ; r3++
X4157:  djnz    r7,X4149        ; Loop 10 times
        mov     r6,b            ; r6 = b
X415b:  mov     r2,#4           ; r2 = 4
X415d:  mov     r1,#3           ; r1 = 3
X415f:  mov     a,@r1           ; a = [r1]
        cjne    r2,#1,X4164     ; if( r2!=1 ) goto X4164
        ret     
;
X4164:  acall   X437d           ; Send byte (0x437d).
        inc     r1              ; r1++
        djnz    r2,X415f        ; Loop r2 times
;
; (Called from fptr table at 0x4000.)
X4169:  mov     dptr,#X0000     ; dptr = 0x0000
        acall   X43ae           ; Check if key checked and receive byte.
        orl     dph,a           ; dph = byte
        mov     r6,#0           ; r6 = 0
X4172:  mov     a,#0ffh         ; a = 0xFF
        db      0a5h            ; Reserved instruction
        nop     
        cjne    r6,#1,X417a     ; if( r6!=1 ) goto X417a
        ret     
X417a:  inc     dptr            ; dptr++
        acall   X437d           ; Send byte (0x437d).
        djnz    r6,X4172        ; Loop 256-1 times
;
; Test internal RAM?
; (Called from fptr table at 0x4000.)
X417f:  mov     dph,rb1r0       ; dph = 0x43 (from dph)
        setb    c               ; Set c
X4183:  mov     r0,#0ffh        ; r0 = 0xFF
X4185:  mov     a,r0            ; a = r0
        jc      X4189           ; \ if( !c ) a = ~a
        cpl     a               ; /
X4189:  mov     @r0,a           ; [r0] = a
        djnz    r0,X4185        ; Loop 255 times
        dec     r0              ; r0 = 0xFF
X418d:  mov     a,@r0           ; a = [r0]
        jc      X4191           ; \ if( !c ) a = ~a
        cpl     a               ; /
X4191:  xrl     a,r0            ; a ^= r0
        jnz     X41a9           ; if( r0!=(~)[r0] ) goto X41a9
        djnz    r0,X418d        ; Loop 255 times
        cpl     c               ; Toggle c
        jnc     X4183           ; if( !c ) goto X4183 (repeat again without ~)
X4199:  cpl     a               ; a = ~a
        mov     dpl,a           ; dpl = a
        dec     r0              ; r0--
X419d:  mov     @r0,a           ; [r0]=a
        djnz    r0,X419d        ; Loop r0 times
        dec     r0              ; r0--
X41a1:  mov     a,@r0           ; a = [r0]
        cjne    a,dpl,X41a9     ; if( a!=dpl ) goto X41a9
        djnz    r0,X41a1        ; Loop r0 times
        jnz     X4199           ; if( a!=0 ) goto X4199
X41a9:  mov     a,r0            ; a = r0
X41aa:  mov     sp,#7           ; [0x81] = 7
        push    dph             ; Save dph to [0x07]?
        ajmp    X403a           ; Command loop
;
; Write into RAM?
; (Called from fptr table at 0x4000.)
X41b1:  acall   X43ae           ; Check if key checked and receive byte.
        mov     adrxh,a         ; [0xB3] = byte1
        mov     b,a             ; b = byte1 (Flags)
        acall   X43ae           ; Check if key checked and receive byte.
        mov     r1,a            ; r1 = byte2 (Start address)
        acall   X43ae           ; Check if key checked and receive byte.
        mov     r4,a            ; r4 = byte3 (Length)
        acall   X4085           ; Verify again mask key.
        mov     r3,a            ; r3 = 0
        mov     dph,rb1r0       ; dph = 0x43 (from dph)
X41c3:  acall   X43ae           ; Check if key checked and receive byte.
        jnb     b.7,X41de       ; if( !b.7 ) goto X41de
        cjne    r1,#5,X41cb     ; \
X41cb:  jc      X41d8           ; / if( ri<5 ) goto X41d8
        ; internal RAM
        mov     @r1,a           ; [r1] = a
        xrl     a,@r1           ; a ^= [r1]
X41cf:  jb      b.6,X41d8       ; if( b.6 ) goto X41d8
        jz      X41d8           ; if( a!=0 (write error?) ) goto X41d8
        mov     a,r1            ; a = r1
        mov     r3,a            ; r3 = r1
        setb    b.6             ; Set b.6
X41d8:  inc     r1              ; r1++
        djnz    r4,X41c3        ; Loop r4 times
        mov     a,r3            ; a = r3
        ajmp    X41aa           ; Fiddle with stack an return to command loop
        ; external RAM/EEPROM
X41de:  movx    @r1,a           ; ext[r1] = a
        mov     r5,a            ; r5 = a
        movx    a,@r1           ; a = ext[r1]
        xrl     a,r5            ; a ^= r5
        sjmp    X41cf
;
; Send byte2-1 bytes starting at address byte1 from internal RAM.
; (Called from fptr table at 0x4000.)
X41e4:  acall   X43ae           ; Check if key checked and receive byte.
        mov     r1,a            ; r1 = byte1 (Address)
        acall   X43ae           ; Check if key checked and receive byte.
        mov     r4,a            ; r4 = byte2 (Length+1)
        acall   X4085           ; Verify again mask key.
        mov     a,r4            ; a = r4
        mov     r2,a            ; r2 = r4
        ajmp    X415f
;
; Read starting at dptr byte1-1 bytes from ROM or EEPROM.
; (Called from fptr table at 0x4000.)
X41f0:  lcall   X02ae           ; Read dptr from IO.
        acall   X43ae           ; Check if key checked and receive byte.
        mov     r5,a            ; r5 = byte1 (Length+1)
        acall   X4085           ; Verify again mask key.
X41f8:  cjne    r4,#17h,X4200   ; if( r4!=0x17 (function number) ) goto X4200
        mov     a,dph           ; a = dph
        jnb     acc.7,X4203     ; if( !acc.7(in EEPROM) ) goto X4203
X4200:  movx    a,@dptr         ; a = ext[dptr]
        sjmp    X4210           ; goto X4210
X4203:  clr     a               ; a = 0
        movc    a,@a+dptr       ; a = rom[dptr]
        mov     r1,a            ; r1 = a
        mov     adrxh,#80h      ; [0xB3]=0x80
        mov     r0,#0           ; r0 = 0
        movx    a,@r0           ; a = [r0]
X420c:  cjne    a,#0a9h,X420c   ; Loop forever if( a!=0xa9 )
        mov     a,r1            ; a = r1
X4210:  cjne    r5,#1,X4214     ; if( r5!=1 ) goto X4214
X4213:  ret     
;
X4214:  acall   X437d           ; Send byte (0x437d).
        inc     dptr            ; dptr++
        djnz    r5,X41f8        ; Loop r5-1 times
;
; Jump to function pointed to by dptr.
; (Called from fptr table at 0x4000.)
X4219:  lcall   X02ae           ; Read dptr from IO.
        acall   X4085           ; Verify again mask key.
        jmp     @a+dptr         ; goto [dptr]
;
; (Called from fptr table at 0x4000.)
X421f:  acall   X40cd
        acall   X4225           ; Compare somehow bytes in EEPROM
        ajmp    X42a1           ; if( a!=0 ) send a and fiddle with [0xD8]
;
; Compare somehow bytes in EEPROM
X4225:  mov     r4,a            ; r4=a (=0?)
        jz      X4239           ; if( a==0 ) goto X4239
        mov     r4,#20h         ; r4=0x20
X422a:  movx    a,@dptr         ; a = ext[0x80??]
        xrl     a,r5            ; a ^= byte1 (Compare byte1 with ext[0x80??])
        jnz     X4242           ; if( a!=0 ) ret
        inc     dptr            ; dptr++
        dec     r4              ; r4--
        cjne    r4,#10h,X4236   ; if( r4!=0x10 ) goto X4236
        xrl     rb0r5,#0ffh     ; r5@rb0 ^= 0xFF
X4236:  cjne    r4,#0,X422a     ; if( r4!=0 ) goto X422a (loop)
X4239:  movx    a,@dptr         ; a = ext[0x80??]
        xrl     a,r6            ; a ^= byte2 (Compare byte2 with ext[0x80??])
        jnz     X4242           ; if( a!=0 ) ret
        inc     dptr            ; dptr++
        djnz    r1,X4239        ; Loop r1 times
        djnz    r2,X4239        ; Loop r2 times
X4242:  ret     
;
X4243:  mov     a,#81h          ; a = 0x81
X4245:  mov     r5,a            ; r5 = a
        mov     r6,#0ffh        ; r6 = 0xFF
        sjmp    X4252           ; goto X4252
X424a:  mov     r5,#80h         ; r5 = 0x80
        mov     r6,#0           ; r6 = 0x00
        sjmp    X4252           ; goto X4252
X4250:  mov     r5,#0           ; r5 = 0
X4252:  acall   X40d3
        acall   X4225           ; Compare somehow bytes in EEPROM
X4256:  ljmp    X02ca           ; Do magic with [0xD8].
;
; (Called from fptr table at 0x4000.)
X4259:  mov     0fdh,#0ach      ; [0xFD] = 0xAC
        acall   X42fa
        mov     0fdh,#0c9h      ; [0xFD] = 0xC9
        acall   X424a
        jz      X426f           ; if( a==0 ) goto X426f
        mov     0fdh,#0d9h      ; [0xFD] = 0xD9
        acall   X4236           ; Compare somehow EEPROM
        jz      X426f           ; if( a==0 ) goto X426f
        mov     0fdh,#0e9h      ; [0xFD] = 0xE9
X426f:  mov     a,#0d0h         ; a = 0xD0
        acall   X42e9
        inc     0fdh            ; [0xFD]++
        clr     a               ; a = 0
        acall   X4245
        jz      X4286           ; if( a==0 ) goto X4286
        inc     0fdh            ; [0xFD]++
        mov     a,#80h          ; a = 0x80
        acall   X4082           ; Do magic with [0xD8].
        acall   X4236           ; Compare somehow EEPROM
        jz      X4286           ; if( a==0 ) goto X4286
        inc     0fdh            ; [0xFD]++
X4286:  mov     dph,#55h        ; dph = 0x55
        mov     dpl,0fdh        ; dpl = [0xFD]
        ajmp    X42a6           ; Send dph and do magic with [0xD8].
;
; (Called from fptr table at 0x4000.)
X428e:  acall   X42f4
        mov     2fh,#0b4h       ; [0x2F] = 0xB4
        clr     a               ; a = 0
        acall   X42e9
        acall   X424a
        jnz     X42a6           ; if( a!=0 ) goto X42a6
        mov     dptr,#X8081     ; dptr = 0x8081
        acall   X42ee
        acall   X424a
X42a1:  jnz     X42a6           ; if( a!=0 ) goto X42a6
        mov     dptr,#X5555     ; dptr = 0x5555
X42a6:  mov     a,dph           ; a = dph
        acall   X437d           ; Send byte (0x437d).
        mov     a,dpl           ; a = dpl
        ajmp    X4256           ; Do magic with [0xD8].
;
; (Called from fptr table at 0x4000.)
X42ae:  acall   X43ae           ; Check if key checked and receive byte.
        push    0fdh            ; Save [0xFD]
        mov     2fh,a           ; [0x2F] = a (bit area)
        anl     2fh,#0f0h       ; [0x2F] &= 0xF0
        mov     r0,#0f0h        ; r0 = 0xF0
        jb      2fh.6,X42bf     ; if( [0x2F].6 ) goto 0x42bf
        mov     r0,#0fh         ; r0 = 0x0F
        swap    a               ; swap nibbles of a
X42bf:  orl     a,r0            ; a |= r0 (0xF0 or 0x0F)
        mov     c,2fh.7         ; c = [0x2F].7
        jc      X42c8           ; if( [0x2F].7 ) goto X42c8
        xrl     a,r0            ; a ^= r0 (0xF0 or 0x0F)
        xch     a,0fdh          ; swap a and [0xFD]
        anl     a,r0            ; a &= r0 (0xF0 or 0x0F)
X42c8:  addc    a,0fdh          ; a += [0xFD] + [0x2F].7
        mov     0fdh,a          ; [0xFD] = a
        setb    2fh.7           ; Set [0x2F].7
        clr     a               ; a = 0
        acall   X42f6
        pop     0fdh            ; Restore [0xFD]
        jb      2fh.6,X42d7     ; if( [0x2F].6 ) goto 0x42D7
        dec     r6              ; r6++
X42d7:  acall   X4250
        ajmp    X42a1
;
; (Called from fptr table at 0x4000.)
X42db:  acall   X42f4
        acall   X424a
        jnz     X42a6           ; if( a ) goto X42a6
        acall   X42e7
        acall   X4243
        ajmp    X42a1           ; goto X42a1
;
X42e7:  mov     a,#30h          ; a = 0x30
X42e9:  mov     dptr,#X8080     ; dptr = 0x8080
        acall   X4082           ; Do magic with [0xD8].
X42ee:  mov     r1,#30h         ; r1 = 0x30
X42f0:  mov     a,r6            ; a = r6
        ljmp    X024f
;
X42f4:  mov     a,#50h          ; a = 0x50
X42f6:  mov     r6,#0           ; r6 = 0
        sjmp    X42e9           ; Goto X42e9
;
X42fa:  mov     r6,#0           ; r6 = 0
        mov     a,#0b0h         ; a = 0xB0
        sjmp    X42e9           ; Goto X42e9
;
; (Called from fptr table at 0x4000.)
X4300:  acall   X40cd
        mov     r7,eepage       ; r7 = [0xFB]
        mov     a,#80h          ; a = 0x80
        mov     b,r7            ; b = [0xFB]
        div     ab              ; a=a/b; b=a mod b
        rl      a               ; a = a<<<1
        mov     r5,a            ; r5 = a
        mov     r1,a            ; r1 = a
X430c:  movx    a,@dptr         ; a = [dptr] (=[0x80??])
        xrl     a,r6            ; \
        jnz     X4330           ; / if( a != r6(byte2) ) goto 0x4330
        inc     dptr            ; dptr++
        jb      b.6,X4323       ; if( b.6 ) next
        mov     a,dpl           ; a = dpl
        cjne    a,#20h,X431d    ; if( dpl!=0x20 ) goto X431d
        setb    b.6             ; Set b.6
        sjmp    X4320           ; Goto X4320
X431d:  cjne    a,#10h,X4323    ; if( dpl!=0x10 ) next
X4320:  xrl     rb0r6,#0ffh     ; r6@rb0 ^= 0xFF
X4323:  djnz    r7,X430c        ; Loop r7 times
        xrl     rb0r6,#0ffh     ; r6@rb0 ^= 0xFF
        mov     r7,eepage       ; r7 = [0xFB]
        djnz    r1,X430c        ; Loop r1 ( (0x80/[0xFB])<<<1 ) times
        mov     r1,rb0r5        ; r1 = r5@rb0
        djnz    r2,X430c        ; Loop r2 ([0xFA]) times
X4330:  ajmp    X42a1           ; Goto X42a1
;
; (Called from fptr table at 0x4000.)
X4332:  mov     r7,#2           ; r7 = 2
        setb    b.0             ; Set b.0
        mov     r6,#0           ; r6 = 0
X4338:  mov     r5,0f9h         ; r5 = [0xF9]
        clr     a               ; a = 0
        mov     adrxh,a         ; [0xB3] = 0
        mov     dptr,#X0000     ; dptr = 0x0000
        mov     r0,a            ; r0 = 0
X4341:  clr     a               ; a = 0
        cjne    r7,#2,X434c     ; if( r7!=2 ) goto X434c
        mov     a,r0            ; a = r0
        jb      b.0,X434b       ; if( b.0 ) goto X434b
        cpl     a               ; a = ~a
        inc     a               ; a++
X434b:  add     a,r5            ; a += r5
X434c:  xrl     a,r6            ; a ^= r6
        jb      b.0,X4358       ; if( b.0 ) goto X4358
        mov     r4,a            ; \
        movx    a,@dptr         ; ) a ^= [dptr] (=0x33?)
        xrl     a,r4            ; /
        jnz     X437b           ; if( a ) goto X437b
        inc     dptr            ; dptr++
        sjmp    X4359           ; goto X4359
X4358:  movx    @r0,a           ; [r0] = a
X4359:  djnz    r0,X4341        ; Loop r0 times
        inc     adrxh           ; [0xB3]++
        djnz    r5,X4341        ; Loop r5 times
        cpl     b.0             ; Toggle b.0
        jnb     b.0,X4338       ; if( !b.0 ) goto X4338
        mov     a,r6            ; a = r6
        cpl     a               ; a = ~a
        mov     r6,a            ; r6 = a
        jnz     X4338           ; if( a ) goto X4338
        djnz    r7,X4338        ; Loop r7 times
        mov     adrxh,#2        ; [0xB3] = 2
        mov     r7,#7           ; r7 = 7
        clr     c               ; Clear c
X4371:  movx    @r0,a           ; [r0] = a
        xch     a,adrxh         ; \
        rlc     a               ; ) rotate left [0xB3] into carry
        xch     a,adrxh         ; /
        djnz    r7,X4371        ; Loop 7 times
        movx    a,@r0           ; a = [r0]
        cpl     a               ; a = ~a
X437b:  ajmp    X42a1           ; Goto X42a1
;
; Send byte (0x437d).
X437d:  anl     94h,#0feh       ; Clear [0x94].0
X4380:  jnb     p1.0,X4380      ; Wait for p1.0 to become 1.
        setb    p1.0            ; Set p1.0
        orl     94h,#1          ; Set [0x94].0
        mov     r0,#0ah
        clr     c
        nop     
        jnb     f0,X43a8        ; Transmit a without setting psw.1.
        jnb     psw.1,X439a     ; Send byte with delay. (4.91MHz?)
        ; Transmit ca. 100000bps@3.57MHz
X4392:  mov     p1.0,c          ; Write p1.0
        rrc     a               ; Rotate right a into c
        xch     a,acc           ; NOP ?
        djnz    r0,X4392        ; Loop 10 times
        ret     
;
; Send byte with delay.
X439a:  acall   X43d2           ; NOP
        acall   X43d1           ; NOP
X439e:  mov     p1.0,c
        rrc     a
        acall   X43d1           ; NOP
        xch     a,@r0           ; NOP ?
        xch     a,@r0           ; NOP ?
        djnz    r0,X439e        ; Loop 10 times
        ret     
;
X43a8:  lcall   X036d           ; Transmit a without setting psw.1.
        ljmp    X03a4           ; Wait dependant on psw.1
;
; Check if mask key has been checked and receive byte.
X43ae:  mov     a,rb1r0         ; a = 0x43 (from dph)
X43b0:  cjne    a,#43h,X43b0    ; Loop forever if a is not 0x43.
X43b3:  anl     94h,#0feh       ; Clear [0x94].0.
        jb      f0,X43bc        ; If( f0 ) jump 0x43bc.
        ljmp    X033e           ; Wait for byte and write it into a.
X43bc:  jnb     p1.0,X43bc      ; Wait for p1.0 set
X43bf:  jb      p1.0,X43bf      ; Wait for p1.0 cleared
        mov     r0,#9
X43c4:  mov     c,p1.0          ; Read p1.0.
        rrc     a               ; Rotate right c into a
        jnb     psw.1,X43d4     ; if( !psw.1 ) do some delay.
X43ca:  djnz    r0,X43c4        ; Loop 9 times
        orl     94h,#1          ; Set [0x94].0.
        clr     p1.0            ; Clear p1.0.
X43d1:  nop     
X43d2:  nop     
X43d3:  ret     
X43d4:  acall   X43d3
        ajmp    X43ca
;
X43d8:  00 00 00 00 00 00 00 00
X43e0:  00 00 00 00 00 00 00 00
X43e8:  00 00 00 00 00 00 00 00
X43f0:  00 00 00 00 00 00 00 72
X43f8:  21 c0 35 08 81 db d5 08

        end