osd-contiki/cpu/6502/net/w5100.S
Oliver Schmidt a5d7a06027 Enhanced Ethernet drivers.
Made Ethernet drivers easier to consume by assembly programs.
* Replaced function pointers with JMP instructions.
* Provide return values additionally via Carry flag.

Reset Ethernet chips on initialization.
Both for the CS8900A and the W5100 the data sheets just say that
the RESET bit is automatically cleared after the RESET. This may
be interpreted in two ways:
1) There's no need to be afraid of reading the RESET bit as 1 and
unintentionally trigger a RESET by writing it back after ORing in
some other bit.
2) The RESET process isn't complete before the RESET bit hasn't
become 0 again.
It's impossible for me to empirically falsify the latter option
as the drivers are supposed to work on faster machines than the
ones I have access to. And if the RESET process includes things
like oscillators then the time to complete the RESET could differ
even between multiple exemplars of the same chip. Therefore I
opted to presume the latter option.
However that means a non-exsistent chip may cause an infinite
loop while waiting for the RESET bit to be cleared so I finally
added code to detect the presence of the Ethernet chips. There's
a risk of a chip being locked up in a way that makes the detection
fail - and therefore the RESET not being performed. This catch-22
needs to be solved by the user doing a hard RESET.
2014-06-12 22:56:35 +02:00

512 lines
10 KiB
ArmAsm

;
; Copyright (c) 2013, Oliver Schmidt
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; 1. Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; 2. Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; 3. Neither the name of the Institute nor the names of its contributors
; may be used to endorse or promote products derived from this software
; without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
; ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
; SUCH DAMAGE.
;
; This file is part of the Contiki operating system.
;
; Author: Oliver Schmidt <ol.sc@web.de>
;
;---------------------------------------------------------------------
.macpack module
module_header _w5100
; Driver signature
.byte $65, $74, $68 ; "eth"
.byte $01 ; Ethernet driver API version number
; Ethernet address
mac: .byte $00, $08, $DC ; OUI of WIZnet
.byte $11, $11, $11
; Buffer attributes
bufaddr:.res 2 ; Address
bufsize:.res 2 ; Size
; Jump table.
jmp init
jmp poll
jmp send
jmp exit
;---------------------------------------------------------------------
.if DYN_DRV
.zeropage
sp: .res 2 ; Stack pointer (Do not trash !)
reg: .res 2 ; Pointer Register content
ptr: .res 2 ; Indirect addressing pointer
len: .res 2 ; Data length
cnt: .res 2 ; Data length counter
adv: .res 2 ; Data pointer advancement
dir: .res 1 ; Transfer direction
bas: .res 1 ; Socket 0 Base Address (hibyte)
lim: .res 1 ; Socket 0 memory limit (hibyte)
tmp: .res 1 ; Temporary value
.else
.include "zeropage.inc"
reg := ptr1 ; Pointer Register content
ptr := ptr2 ; Indirect addressing pointer
len := ptr3 ; Data length
cnt := ptr4 ; Data length counter
adv := sreg ; Data pointer advancement
dir := tmp1 ; Transfer direction
bas := tmp2 ; Socket 0 Base Address (hibyte)
lim := tmp3 ; Socket 0 memory limit (hibyte)
tmp := tmp4 ; Temporary value
.endif
;---------------------------------------------------------------------
.rodata
fixup: .byte fixup02-fixup01, fixup03-fixup02, fixup04-fixup03
.byte fixup05-fixup04, fixup06-fixup05, fixup07-fixup06
.byte fixup08-fixup07, fixup09-fixup08, fixup10-fixup09
.byte fixup11-fixup10, fixup12-fixup11, fixup13-fixup12
.byte fixup14-fixup13, fixup15-fixup14, fixup16-fixup15
.byte fixup17-fixup16, fixup18-fixup17, fixup19-fixup18
.byte fixup20-fixup19, fixup21-fixup20, fixup22-fixup21
.byte fixup23-fixup22, fixup24-fixup23, fixup25-fixup24
.byte fixup26-fixup25, fixup27-fixup26, fixup28-fixup27
.byte fixup29-fixup28, fixup30-fixup29
fixups = * - fixup
;---------------------------------------------------------------------
mode := $FF00 ; High byte patched at runtime
addr := $FF01 ; High byte patched at runtime
data := $FF03 ; High byte patched at runtime
.data
;---------------------------------------------------------------------
init:
; Save address of register base
sta reg
stx reg+1
; Start with first fixup location
lda #<(fixup01+1)
ldx #>(fixup01+1)
sta ptr
stx ptr+1
ldx #$FF
ldy #$00
; Fixup address at location
: lda reg
ora (ptr),y
sta (ptr),y
iny
lda reg+1
sta (ptr),y
dey
; Advance to next fixup location
inx
cpx #fixups
bcs :+
lda ptr
clc
adc fixup,x
sta ptr
bcc :-
inc ptr+1
bcs :- ; Always
; Indirect Bus I/F mode, Address Auto-Increment
:
fixup01:lda mode
ora #$03
fixup02:sta mode
; Retry Time-value Register: = 2000 ?
ldx #$00 ; Hibyte
ldy #$17 ; Lobyte
jsr set_addr
lda #$07^$D0
fixup03:eor data
fixup04:eor data
beq :+
sec
rts
; S/W Reset
: lda #$80
fixup05:sta mode
:
fixup06:lda mode
bmi :-
; Indirect Bus I/F mode, Address Auto-Increment, Ping Block
lda #$13
fixup07:sta mode
; Source Hardware Address Register: MAC Address
ldx #$00 ; Hibyte
ldy #$09 ; Lobyte
jsr set_addr
: lda mac,x
fixup08:sta data
inx
cpx #$06
bcc :-
; RX Memory Size Register: Assign 8KB to socket 0
; TX Memory Size Register: Assign 8KB to socket 0
ldx #$00 ; Hibyte
ldy #$1A ; Lobyte
jsr set_addr
lda #$03
fixup09:sta data
fixup10:sta data
; Socket 0 Mode Register: MACRAW, MAC Filter
; Socket 0 Command Register: OPEN
ldy #$00
jsr set_addrsocket0
lda #$44
fixup11:sta data
lda #$01
fixup12:sta data
tya
tax
clc
rts
;---------------------------------------------------------------------
poll:
; Check for completion of previous command
; Socket 0 Command Register: = 0 ?
jsr set_addrcmdreg0
fixup13:lda data
beq :++
; No data available
lda #$00
: tax
sec
rts
; Socket 0 RX Received Size Register: != 0 ?
: ldy #$26 ; Socket RX Received Size Register
jsr set_addrsocket0
fixup14:lda data ; Hibyte
fixup15:ora data ; Lobyte
beq :--
; Process the incoming data
; -------------------------
; Set parameters for receiving data
lda #>$6000 ; Socket 0 RX Base Address
ldx #$00 ; Read
jsr set_parameters
; ldy #$28 ; Socket RX Read Pointer Register
; jsr set_addrsocket0
; Calculate and set physical address
jsr set_addrphysical
; Move physical address shadow to $E000-$FFFF
ora #>$8000
tax
; Read MAC raw 2byte packet size header
jsr get_datacheckaddr ; Hibyte
sta adv+1
jsr get_datacheckaddr ; Lobyte
sta adv
; Subtract 2byte header and set length
sec
sbc #<$0002
sta len
sta cnt
lda adv+1
sbc #>$0002
sta len+1
sta cnt+1
; Is bufsize < length ?
lda bufsize
cmp len
lda bufsize+1
sbc len+1
bcs :+
; Set data length = 0 and skip read
lda #$00
sta len
sta len+1
beq :++ ; Always
; Read data
: jsr mov_data
; Set parameters for common code
: lda #$40 ; RECV
ldy #$28 ; Socket 0 RX Read Pointer Register
; Advance pointer register
common: jsr set_addrsocket0
tay ; Save command
clc
lda reg
adc adv
tax
lda reg+1
adc adv+1
fixup16:sta data ; Hibyte
fixup17:stx data ; Lobyte
; Set command register
tya ; Restore command
jsr set_addrcmdreg0
fixup18:sta data
; Return data length (will be ignored for send)
lda len
ldx len+1
clc
rts
;---------------------------------------------------------------------
send:
; Save data length
sta len
stx len+1
sta cnt
stx cnt+1
sta adv
stx adv+1
; Set parameters for transmitting data
lda #>$4000 ; Socket 0 TX Base Address
ldx #$01 ; Write
jsr set_parameters
; Wait for completion of previous command
; Socket 0 Command Register: = 0 ?
: jsr set_addrcmdreg0
fixup19:lda data
bne :-
; Socket 0 TX Free Size Register: < length ?
: ldy #$20 ; Socket TX Free Size Register
jsr set_addrsocket0
fixup20:lda data ; Hibyte
fixup21:ldx data ; Lobyte
cpx len
sbc len+1
bcc :-
; Send the data
; -------------
ldy #$24 ; Socket TX Write Pointer Register
jsr set_addrsocket0
; Calculate and set pyhsical address
jsr set_addrphysical
; Write data
jsr mov_data
; Set parameters for common code
lda #$20 ; SEND
ldy #$24 ; Socket TX Write Pointer Register
bne common ; Always
;---------------------------------------------------------------------
exit:
rts
;---------------------------------------------------------------------
set_addrphysical:
fixup22:lda data ; Hibyte
fixup23:ldy data ; Lobyte
sty reg
sta reg+1
and #>$1FFF ; Socket Mask Address (hibyte)
ora bas ; Socket Base Address (hibyte)
tax
set_addr:
fixup24:stx addr ; Hibyte
fixup25:sty addr+1 ; Lobyte
rts
set_addrcmdreg0:
ldy #$01 ; Socket Command Register
set_addrsocket0:
ldx #>$0400 ; Socket 0 register base address
bne set_addr ; Always
set_addrbase:
ldx bas ; Socket Base Address (hibyte)
ldy #<$0000 ; Socket Base Address (lobyte)
beq set_addr ; Always
get_datacheckaddr:
fixup26:lda data
iny ; Physical address shadow (lobyte)
bne :+
inx ; Physical address shadow (hibyte)
beq set_addrbase
: rts
;---------------------------------------------------------------------
set_parameters:
; Setup variables in zero page
sta bas ; Socket Base Address
clc
adc #>$2000 ; Socket memory size
sta lim ; Socket memory limit
stx dir ; Transfer direction
; Set indirect addressing pointer
lda bufaddr
ldx bufaddr+1
sta ptr
stx ptr+1
rts
;---------------------------------------------------------------------
mov_data:
; Calculate highest R/W address allowing
; to R/W without address wraparound
sec
lda #<$0000 ; Socket memory limit (lobyte)
sbc len
tay
lda lim ; Socket memory limit (hibyte)
sbc len+1
tax
tya
; R/W without address wraparound possible because
; highest R/W address > actual R/W address ?
; sec
fixup27:sbc addr+1 ; Lobyte
tay
txa
fixup28:sbc addr ; Hibyte
tax
tya
bcs :+
; Calculate length of first chunk
; clc
adc len
sta cnt
tay
txa
adc len+1
sta cnt+1
tax
tya
; R/W first chunk
jsr rw_data
; Wraparound R/W address
jsr set_addrbase
; Set buffer pointer for second chunk
clc
lda bufaddr
adc cnt
sta ptr
lda bufaddr+1
adc cnt+1
sta ptr+1
; Calculate length of second chunk
sec
lda len
sbc cnt
sta cnt
lda len+1
sbc cnt+1
sta cnt+1
; Get length of (second) chunk
: lda cnt
ldx cnt+1
; R/W (second) chunk
rw_data:eor #$FF ; Two's complement part 1
tay
iny ; Two's complement part 2
sty tmp
sec
lda ptr
sbc tmp
sta ptr
lda ptr+1
sbc #$00
sta ptr+1
lda dir ; Transfer direction
bne :++
; Read data
:
fixup29:lda data
sta (ptr),y
iny
bne :-
inc ptr+1
dex
bpl :-
rts
; Write data
: lda (ptr),y
fixup30:sta data
iny
bne :-
inc ptr+1
dex
bpl :-
rts
;---------------------------------------------------------------------