diff --git a/src/aliases/,aliases_hardware_arnitech.s b/src/aliases/,aliases_hardware_arnitech.s index ef5f1366..134ceb15 100644 --- a/src/aliases/,aliases_hardware_arnitech.s +++ b/src/aliases/,aliases_hardware_arnitech.s @@ -23,4 +23,13 @@ !addr ARNITECH_CLOCK_HRS = $DE94 ; hours, BCD 1-12, bit 7 PM !addr ARNITECH_CARD_WINDOW = $DEA0 ; slot n's Card window at $DEA0 + 16 * n + + ; The RR-Net socket: the CS8900A of the Network Card in the lowest slot, in RR-Net order + ; (arnitech's ADR 0027; Cirrus Logic DS271F5 Table 18, each port's even byte the low one) + + !addr RRNET_PP_POINTER = $DE02 ; PacketPage pointer + !addr RRNET_PP_DATA = $DE04 ; PacketPage data port 0 + !addr RRNET_RX_TX_DATA = $DE08 ; receive and transmit data port 0 + !addr RRNET_TX_CMD = $DE0C ; write-only + !addr RRNET_TX_LENGTH = $DE0E ; write-only } diff --git a/src/kernal/arnitech/arnitech_net.s b/src/kernal/arnitech/arnitech_net.s new file mode 100644 index 00000000..a4a9c26f --- /dev/null +++ b/src/kernal/arnitech/arnitech_net.s @@ -0,0 +1,298 @@ +;; #LAYOUT# STD * #TAKE +;; #LAYOUT# * * #IGNORE + +; +; The network entries of the Arnitech jump table (mc64 only, arnitech's ADR 0027): whole +; Ethernet frames through the CS8900A at the RR-Net socket, polled, from the Destination +; Address to the end of the data, without the CRC. The chip is found by its Product ID, so +; the routines find an RR-Net on any C64 that has one. Register semantics are the CS8900A +; datasheet's (Cirrus Logic DS271F5) and its 8-bit mode's (AN181). +; +; SAL holds the pointer to the frame or buffer, EAL the bytes left, as LOAD and SAVE use them. +; + +!ifdef CONFIG_ARNITECH_CONTROLLER { + + ; PacketPage addresses, all but the Product ID's in page $01 + CS_PRODUCT_ID = $630E ; at $0000, low byte first + CS_RX_CFG = $02 ; bit 6 Skip_1 + CS_RX_CTL = $04 + CS_LINE_CTL = $12 + CS_SELF_CTL = $14 ; bit 6 RESET + CS_RX_EVENT = $24 ; bit 8 RxOK + CS_SELF_ST = $36 ; bit 7 INITD + CS_BUS_ST = $38 ; bit 7 TxBidErr, bit 8 Rdy4TxNOW + CS_IA = $58 ; Individual Address, $0158-$015D + + CS_RX_CTL_VALUE = $0D05 ; RxOKA, IndividualA, BroadcastA + CS_LINE_VALUE = $00D3 ; SerRxON, SerTxON + CS_TX_AFTER_ALL = $C0 ; TxCMD: start once the whole frame is in the chip + + NET_NONE_WAITING = $00 + NET_NO_CARD = $01 + NET_NO_ROOM = $02 + NET_LENGTH_REFUSED = $03 + NET_TOO_LONG = $04 + +; +; NET_INIT, $FF0C: see ff00.arnitech_jumptable.s +; + +arnitech_net_init: + + jsr arnitech_net_probe + bcs @2 + + lda #CS_SELF_CTL + jsr arnitech_net_point + lda #$40 ; RESET, which points the PacketPage pointer at $0000 + sta RRNET_PP_DATA + + lda #CS_SELF_ST + jsr arnitech_net_point +@1: + lda RRNET_PP_DATA + bpl @1 ; until INITD + + +phy_trash_a + lda #CS_RX_CTL + ldx #CS_RX_CTL_VALUE + jsr arnitech_net_write + lda #CS_LINE_CTL + ldx #CS_LINE_VALUE + jsr arnitech_net_write + +ply_trash_a + + clc +@2: + rts + +; +; NET_MAC, $FF0F: see ff00.arnitech_jumptable.s +; + +arnitech_net_mac: + + stx SAL+0 + sty SAL+1 + jsr arnitech_net_probe + bcs @2 + + ldy #$00 +@1: + tya + ora #CS_IA ; $58, $5A, $5C + jsr arnitech_net_point + lda RRNET_PP_DATA+0 + sta (SAL), y + iny + lda RRNET_PP_DATA+1 + sta (SAL), y + iny + cpy #$06 + bne @1 + + clc +@2: + rts + +; +; NET_SEND, $FF12: see ff00.arnitech_jumptable.s +; + +arnitech_net_send: + + jsr arnitech_net_args + lda #NET_NO_CARD + bcs arnitech_net_done + + lda #CS_TX_AFTER_ALL ; TxCMD, which the high byte's write gives the chip + sta RRNET_TX_CMD+0 + lda #$00 + sta RRNET_TX_CMD+1 + lda EAL+0 ; TxLength: the bid, at the high byte's write + sta RRNET_TX_LENGTH+0 + lda EAL+1 + sta RRNET_TX_LENGTH+1 + + lda #CS_BUS_ST + jsr arnitech_net_point + lda RRNET_PP_DATA+0 + bmi @1 ; TxBidErr + lda RRNET_PP_DATA+1 + lsr ; Rdy4TxNOW into carry + lda #NET_NO_ROOM + bcc arnitech_net_fail + + clv ; memory to chip + jmp arnitech_net_copy ; carry clear +@1: + lda #NET_LENGTH_REFUSED + + ; FALLTROUGH + +arnitech_net_fail: + + sec + + ; FALLTROUGH + +arnitech_net_done: + + rts + +; +; NET_RECV, $FF15: see ff00.arnitech_jumptable.s +; + +arnitech_net_recv: + + jsr arnitech_net_args + lda #NET_NO_CARD + bcs arnitech_net_done + + lda #CS_RX_EVENT + jsr arnitech_net_point + lda RRNET_PP_DATA+1 ; reading the event clears it + lsr ; RxOK into carry + lda #NET_NONE_WAITING + bcc arnitech_net_fail + + lda RRNET_RX_TX_DATA+1 ; RxStatus, high byte first, not needed + lda RRNET_RX_TX_DATA+0 + ldx RRNET_RX_TX_DATA+1 ; RxLength, high byte first + lda RRNET_RX_TX_DATA+0 + + cpx EAL+1 + bne @1 + cmp EAL+0 + beq @2 +@1: + bcs @3 ; longer than the buffer +@2: + sta EAL+0 + stx EAL+1 + pha + txa + pha + bit arnitech_net_copy_end ; an RTS, $60: sets V, chip to memory + jsr arnitech_net_copy + pla + tay + pla + tax + clc + rts +@3: + lda #CS_RX_CFG + jsr arnitech_net_point + lda RRNET_PP_DATA+0 + ora #$40 ; Skip_1: the frame is dropped + sta RRNET_PP_DATA+0 + lda #NET_TOO_LONG + bne arnitech_net_fail ; branch always + +; +; The pointer from the zero page address in A into SAL, the length in X/Y into EAL, and the +; chip probed. Out: carry clear if it answers. +; + +arnitech_net_args: + + stx EAL+0 + sty EAL+1 + tax + lda $00, x + sta SAL+0 + lda $01, x + sta SAL+1 + + ; FALLTROUGH + +; +; Whether the chip answers: its Product ID at PacketPage $0000. Out: carry clear if it does. +; X and Y are kept. +; + +arnitech_net_probe: + + lda #$00 + sta RRNET_PP_POINTER+0 + sta RRNET_PP_POINTER+1 + lda RRNET_PP_DATA+0 + cmp #CS_PRODUCT_ID + bne @1 + clc + rts +@1: + sec + rts + +; +; PacketPage register $01xx, xx in A, written with X (low byte) and Y (high byte) +; + +arnitech_net_write: + + jsr arnitech_net_point + stx RRNET_PP_DATA+0 + sty RRNET_PP_DATA+1 + rts + +; +; The PacketPage pointer at $01xx, xx in A. X and Y are kept. +; + +arnitech_net_point: + + sta RRNET_PP_POINTER+0 + lda #$01 + sta RRNET_PP_POINTER+1 + rts + +; +; EAL bytes between the chip's receive and transmit data port and SAL: from the chip with V +; set, to it with V clear. Even bytes go through the port's low byte, odd ones through its +; high byte. Out: carry clear. +; + +arnitech_net_copy: + + ldy #$00 +@1: + lda EAL+0 + ora EAL+1 + beq @4 + tya + and #$01 + tax + bvs @2 + lda (SAL), y + sta RRNET_RX_TX_DATA, x + bvc @3 ; branch always +@2: + lda RRNET_RX_TX_DATA, x + sta (SAL), y +@3: + iny + bne @5 + inc SAL+1 +@5: + lda EAL+0 + bne @6 + dec EAL+1 +@6: + dec EAL+0 + jmp @1 +@4: + clc + +arnitech_net_copy_end: + + rts +} diff --git a/src/kernal/arnitech/ff00.arnitech_jumptable.s b/src/kernal/arnitech/ff00.arnitech_jumptable.s index f57b4ce7..bad16b28 100644 --- a/src/kernal/arnitech/ff00.arnitech_jumptable.s +++ b/src/kernal/arnitech/ff00.arnitech_jumptable.s @@ -9,11 +9,12 @@ ; stay clear, for the stock IRQ entry, CINT, and the KERNAL revision byte. ; ; $FF00-$FF03 "ARNI" -; $FF04 version of the table, $01 +; $FF04 version of the table, $02 ; $FF05 number of entries ; ; $FF06 ARNI_FINDCARD - finds the first Card slot holding a Card of a given type -; In: A = the Card's type ($01 Redstone Card, $02 Internet Card; $00 finds an empty slot) +; In: A = the Card's type ($01 Redstone Card, $02 Internet Card, $03 Network Card; +; $00 finds an empty slot) ; Out: carry clear if found: A = the slot (0-5), X = the low byte of its Card window ; ($DEA0 + 16 * slot, so LDA $DE00,X reads the window's first byte); ; carry set if no slot holds one, or no controller answers (a Cartridge switched it off) @@ -26,15 +27,50 @@ ; untouched but on 50 Hz all the same ; A and X are not kept, Y is ; +; Version 2 adds the network: whole Ethernet frames, from the Destination Address to the end of +; the data without the CRC, through the CS8900A at the RR-Net socket (arnitech's ADR 0027), +; found by its Product ID. Software polls; ARP and IP are its own. +; +; $FF0C NET_INIT - resets the chip and sets it to receive frames for its own address and +; broadcasts, and to send +; In: nothing +; Out: carry clear if done; carry set if there is no Network Card +; A and X are not kept, Y is +; +; $FF0F NET_MAC - copies the MAC address: the chip's Individual Address, which a reset sets to +; the Network Card's +; In: X/Y = the address of 6 bytes for it, low byte in X +; Out: carry clear if copied; carry set if there is no Network Card +; A, X, and Y are not kept +; +; $FF12 NET_SEND - sends a frame +; In: A = a zero page address holding a pointer to the frame, +; X/Y = its length, 14-1514, low byte in X; the chip pads one under 60 bytes +; Out: carry clear if sent; carry set if not, with A = 1 no Network Card, 2 no room in +; the chip while it holds frames received, 3 the chip refuses the length +; A, X, and Y are not kept +; +; $FF15 NET_RECV - takes the oldest frame received +; In: A = a zero page address holding a pointer to a buffer, +; X/Y = its length, low byte in X +; Out: carry clear if a frame was copied into the buffer, with X/Y = its length, low byte +; in X; carry set if not, with A = 0 none waiting, 1 no Network Card, 4 the frame is +; longer than the buffer, and is dropped +; A is not kept when a frame is copied +; !ifdef CONFIG_ARNITECH_CONTROLLER { arnitech_jumptable: !byte $41, $52, $4E, $49 ; "ARNI" - !byte $01 ; version - !byte $02 ; entries + !byte $02 ; version + !byte $06 ; entries jmp arnitech_find_card ; $FF06 jmp arnitech_set_tod ; $FF09 + jmp arnitech_net_init ; $FF0C + jmp arnitech_net_mac ; $FF0F + jmp arnitech_net_send ; $FF12 + jmp arnitech_net_recv ; $FF15 }