; interrupt handling assumes that the 65816 is in 6502-emulation mode.
;
- .include "zeropage.inc"
- .include "ser-kernel.inc"
- .include "ser-error.inc"
+ .include "zeropage.inc"
+ .include "ser-kernel.inc"
+ .include "ser-error.inc"
; ------------------------------------------------------------------------
; Header. Includes jump table
- .segment "JUMPTABLE"
+ .segment "JUMPTABLE"
- ; Driver signature
- .byte $73, $65, $72 ; "ser"
- .byte SER_API_VERSION ; Serial API version number
+ ; Driver signature
+ .byte $73, $65, $72 ; "ser"
+ .byte SER_API_VERSION ; Serial API version number
- ; Jump table.
- .addr INSTALL
- .addr UNINSTALL
- .addr OPEN
- .addr CLOSE
- .addr GET
- .addr PUT
- .addr STATUS
- .addr IOCTL
- .addr IRQ
+ ; Library reference
+ .addr $0000
+
+ ; Jump table
+ .addr INSTALL
+ .addr UNINSTALL
+ .addr OPEN
+ .addr CLOSE
+ .addr GET
+ .addr PUT
+ .addr STATUS
+ .addr IOCTL
+ .addr IRQ
;----------------------------------------------------------------------------
; I/O definitions
-ACIA = $031C
-ACIA_DATA = ACIA+0 ; Data register
-ACIA_STATUS = ACIA+1 ; Status register
-ACIA_CMD = ACIA+2 ; Command register
-ACIA_CTRL = ACIA+3 ; Control register
+ACIA = $031C
+ACIA_DATA = ACIA+0 ; Data register
+ACIA_STATUS = ACIA+1 ; Status register
+ACIA_CMD = ACIA+2 ; Command register
+ACIA_CTRL = ACIA+3 ; Control register
;----------------------------------------------------------------------------
; Global variables
- .bss
-
-RecvHead: .res 1 ; Head of receive buffer
-RecvTail: .res 1 ; Tail of receive buffer
-RecvFreeCnt: .res 1 ; Number of bytes in receive buffer
-SendHead: .res 1 ; Head of send buffer
-SendTail: .res 1 ; Tail of send buffer
-SendFreeCnt: .res 1 ; Number of bytes in send buffer
-
-Stopped: .res 1 ; Flow-stopped flag
-RtsOff: .res 1 ;
-
-RecvBuf: .res 256 ; Receive buffers: 256 bytes
-SendBuf: .res 256 ; Send buffers: 256 bytes
-
-Index: .res 1 ; I/O register index
-
- .rodata
-
- ; Tables used to translate RS232 params into register values
-BaudTable: ; bit7 = 1 means setting is invalid
- .byte $FF ; SER_BAUD_45_5
- .byte $01 ; SER_BAUD_50
- .byte $02 ; SER_BAUD_75
- .byte $03 ; SER_BAUD_110
- .byte $04 ; SER_BAUD_134_5
- .byte $05 ; SER_BAUD_150
- .byte $06 ; SER_BAUD_300
- .byte $07 ; SER_BAUD_600
- .byte $08 ; SER_BAUD_1200
- .byte $09 ; SER_BAUD_1800
- .byte $0A ; SER_BAUD_2400
- .byte $0B ; SER_BAUD_3600
- .byte $0C ; SER_BAUD_4800
- .byte $0D ; SER_BAUD_7200
- .byte $0E ; SER_BAUD_9600
- .byte $0F ; SER_BAUD_19200
- .byte $FF ; SER_BAUD_38400
- .byte $FF ; SER_BAUD_57600
- .byte $FF ; SER_BAUD_115200
- .byte $FF ; SER_BAUD_230400
+ .bss
+
+RecvHead: .res 1 ; Head of receive buffer
+RecvTail: .res 1 ; Tail of receive buffer
+RecvFreeCnt: .res 1 ; Number of bytes in receive buffer
+SendHead: .res 1 ; Head of send buffer
+SendTail: .res 1 ; Tail of send buffer
+SendFreeCnt: .res 1 ; Number of bytes in send buffer
+
+Stopped: .res 1 ; Flow-stopped flag
+RtsOff: .res 1 ;
+
+RecvBuf: .res 256 ; Receive buffers: 256 bytes
+SendBuf: .res 256 ; Send buffers: 256 bytes
+
+Index: .res 1 ; I/O register index
+
+ .rodata
+
+ ; Tables used to translate RS232 params into register values
+BaudTable: ; bit7 = 1 means setting is invalid
+ .byte $FF ; SER_BAUD_45_5
+ .byte $01 ; SER_BAUD_50
+ .byte $02 ; SER_BAUD_75
+ .byte $03 ; SER_BAUD_110
+ .byte $04 ; SER_BAUD_134_5
+ .byte $05 ; SER_BAUD_150
+ .byte $06 ; SER_BAUD_300
+ .byte $07 ; SER_BAUD_600
+ .byte $08 ; SER_BAUD_1200
+ .byte $09 ; SER_BAUD_1800
+ .byte $0A ; SER_BAUD_2400
+ .byte $0B ; SER_BAUD_3600
+ .byte $0C ; SER_BAUD_4800
+ .byte $0D ; SER_BAUD_7200
+ .byte $0E ; SER_BAUD_9600
+ .byte $0F ; SER_BAUD_19200
+ .byte $FF ; SER_BAUD_38400
+ .byte $FF ; SER_BAUD_57600
+ .byte $FF ; SER_BAUD_115200
+ .byte $FF ; SER_BAUD_230400
BitTable:
- .byte $60 ; SER_BITS_5
- .byte $40 ; SER_BITS_6
- .byte $20 ; SER_BITS_7
- .byte $00 ; SER_BITS_8
+ .byte $60 ; SER_BITS_5
+ .byte $40 ; SER_BITS_6
+ .byte $20 ; SER_BITS_7
+ .byte $00 ; SER_BITS_8
StopTable:
- .byte $00 ; SER_STOP_1
- .byte $80 ; SER_STOP_2
+ .byte $00 ; SER_STOP_1
+ .byte $80 ; SER_STOP_2
ParityTable:
- .byte $00 ; SER_PAR_NONE
- .byte $20 ; SER_PAR_ODD
- .byte $60 ; SER_PAR_EVEN
- .byte $A0 ; SER_PAR_MARK
- .byte $E0 ; SER_PAR_SPACE
+ .byte $00 ; SER_PAR_NONE
+ .byte $20 ; SER_PAR_ODD
+ .byte $60 ; SER_PAR_EVEN
+ .byte $A0 ; SER_PAR_MARK
+ .byte $E0 ; SER_PAR_SPACE
- .code
+ .code
;----------------------------------------------------------------------------
; INSTALL: Is called after the driver is loaded into memory. If possible,
INSTALL:
UNINSTALL:
CLOSE:
- ldx Index ; Check for open port
- beq :+
+ ldx Index ; Check for open port
+ beq :+
- ; Deactivate DTR and disable 6551 interrupts
- lda #%00001010
- sta ACIA_CMD,x
+ ; Deactivate DTR and disable 6551 interrupts
+ lda #%00001010
+ sta ACIA_CMD,x
- ; Done, return an error code
-: lda #<SER_ERR_OK
- tax ; A is zero
- stx Index ; Mark port as closed
- rts
+ ; Done, return an error code
+: lda #<SER_ERR_OK
+ tax ; A is zero
+ stx Index ; Mark port as closed
+ rts
;----------------------------------------------------------------------------
; OPEN: A pointer to a ser_params structure is passed in ptr1.
; Must return an SER_ERR_xx code in a/x.
OPEN:
- ; Check if the handshake setting is valid
- ldy #SER_PARAMS::HANDSHAKE ; Handshake
- lda (ptr1),y
- cmp #SER_HS_HW ; This is all we support
- bne InvParam
-
- ; Initialize buffers
- ldy #$00
- sty Stopped
- sty RecvHead
- sty RecvTail
- sty SendHead
- sty SendTail
- dey ; Y = 255
- sty RecvFreeCnt
- sty SendFreeCnt
-
- ; Set the value for the control register, which contains stop bits,
- ; word length and the baud rate.
- ldy #SER_PARAMS::BAUDRATE
- lda (ptr1),y ; Baudrate index
- tay
- lda BaudTable,y ; Get 6551 value
- bmi InvBaud ; Branch if rate not supported
- sta tmp1
-
- ldy #SER_PARAMS::DATABITS ; Databits
- lda (ptr1),y
- tay
- lda BitTable,y
- ora tmp1
- sta tmp1
-
- ldy #SER_PARAMS::STOPBITS ; Stopbits
- lda (ptr1),y
- tay
- lda StopTable,y
- ora tmp1
- ora #%00010000 ; Receiver clock source = baudrate
- sta ACIA_CTRL
-
- ; Set the value for the command register. We remember the base value
- ; in RtsOff, since we will have to manipulate ACIA_CMD often.
- ldy #SER_PARAMS::PARITY ; Parity
- lda (ptr1),y
- tay
- lda ParityTable,y
- ora #%00000001 ; DTR active
- sta RtsOff
- ora #%00001000 ; Enable receive interrupts
- sta ACIA_CMD
-
- ; Done
- stx Index ; Mark port as open
- lda #<SER_ERR_OK
- tax ; A is zero
- rts
-
- ; Invalid parameter
-InvParam:lda #<SER_ERR_INIT_FAILED
- ldx #>SER_ERR_INIT_FAILED
- rts
-
- ; Baud rate not available
-InvBaud:lda #<SER_ERR_BAUD_UNAVAIL
- ldx #>SER_ERR_BAUD_UNAVAIL
- rts
+ ; Check if the handshake setting is valid
+ ldy #SER_PARAMS::HANDSHAKE ; Handshake
+ lda (ptr1),y
+ cmp #SER_HS_HW ; This is all we support
+ bne InvParam
+
+ ; Initialize buffers
+ ldy #$00
+ sty Stopped
+ sty RecvHead
+ sty RecvTail
+ sty SendHead
+ sty SendTail
+ dey ; Y = 255
+ sty RecvFreeCnt
+ sty SendFreeCnt
+
+ ; Set the value for the control register, which contains stop bits,
+ ; word length and the baud rate.
+ ldy #SER_PARAMS::BAUDRATE
+ lda (ptr1),y ; Baudrate index
+ tay
+ lda BaudTable,y ; Get 6551 value
+ bmi InvBaud ; Branch if rate not supported
+ sta tmp1
+
+ ldy #SER_PARAMS::DATABITS ; Databits
+ lda (ptr1),y
+ tay
+ lda BitTable,y
+ ora tmp1
+ sta tmp1
+
+ ldy #SER_PARAMS::STOPBITS ; Stopbits
+ lda (ptr1),y
+ tay
+ lda StopTable,y
+ ora tmp1
+ ora #%00010000 ; Receiver clock source = baudrate
+ sta ACIA_CTRL
+
+ ; Set the value for the command register. We remember the base value
+ ; in RtsOff, since we will have to manipulate ACIA_CMD often.
+ ldy #SER_PARAMS::PARITY ; Parity
+ lda (ptr1),y
+ tay
+ lda ParityTable,y
+ ora #%00000001 ; DTR active
+ sta RtsOff
+ ora #%00001000 ; Enable receive interrupts
+ sta ACIA_CMD
+
+ ; Done
+ stx Index ; Mark port as open
+ lda #<SER_ERR_OK
+ tax ; A is zero
+ rts
+
+ ; Invalid parameter
+InvParam:lda #<SER_ERR_INIT_FAILED
+ ldx #>SER_ERR_INIT_FAILED
+ rts
+
+ ; Baud rate not available
+InvBaud:lda #<SER_ERR_BAUD_UNAVAIL
+ ldx #>SER_ERR_BAUD_UNAVAIL
+ rts
;----------------------------------------------------------------------------
; GET: Will fetch a character from the receive buffer and store it into the
; returned.
GET:
- ldy SendFreeCnt ; Send data if necessary
- iny ; Y == $FF?
- beq :+
- lda #$00 ; TryHard = false
- jsr TryToSend
-
- ; Check for buffer empty
-: lda RecvFreeCnt ; (25)
- cmp #$FF
- bne :+
- lda #<SER_ERR_NO_DATA
- ldx #>SER_ERR_NO_DATA
- rts
-
- ; Check for flow stopped & enough free: release flow control
-: ldy Stopped ; (34)
- beq :+
- cmp #63
- bcc :+
- lda #$00
- sta Stopped
- lda RtsOff
- ora #%00001000
- sta ACIA_CMD
-
- ; Get byte from buffer
-: ldy RecvHead ; (41)
- lda RecvBuf,y
- inc RecvHead
- inc RecvFreeCnt
- ldx #$00 ; (59)
- sta (ptr1,x)
- txa ; Return code = 0
- rts
+ ldy SendFreeCnt ; Send data if necessary
+ iny ; Y == $FF?
+ beq :+
+ lda #$00 ; TryHard = false
+ jsr TryToSend
+
+ ; Check for buffer empty
+: lda RecvFreeCnt ; (25)
+ cmp #$FF
+ bne :+
+ lda #<SER_ERR_NO_DATA
+ ldx #>SER_ERR_NO_DATA
+ rts
+
+ ; Check for flow stopped & enough free: release flow control
+: ldy Stopped ; (34)
+ beq :+
+ cmp #63
+ bcc :+
+ lda #$00
+ sta Stopped
+ lda RtsOff
+ ora #%00001000
+ sta ACIA_CMD
+
+ ; Get byte from buffer
+: ldy RecvHead ; (41)
+ lda RecvBuf,y
+ inc RecvHead
+ inc RecvFreeCnt
+ ldx #$00 ; (59)
+ sta (ptr1,x)
+ txa ; Return code = 0
+ rts
;----------------------------------------------------------------------------
; PUT: Output character in A.
; Must return an SER_ERR_xx code in a/x.
PUT:
- ; Try to send
- ldy SendFreeCnt
- iny ; Y = $FF?
- beq :+
- pha
- lda #$00 ; TryHard = false
- jsr TryToSend
- pla
-
- ; Put byte into send buffer & send
-: ldy SendFreeCnt
- bne :+
- lda #<SER_ERR_OVERFLOW
- ldx #>SER_ERR_OVERFLOW
- rts
-
-: ldy SendTail
- sta SendBuf,y
- inc SendTail
- dec SendFreeCnt
- lda #$FF ; TryHard = true
- jsr TryToSend
- lda #<SER_ERR_OK
- tax
- rts
+ ; Try to send
+ ldy SendFreeCnt
+ iny ; Y = $FF?
+ beq :+
+ pha
+ lda #$00 ; TryHard = false
+ jsr TryToSend
+ pla
+
+ ; Put byte into send buffer & send
+: ldy SendFreeCnt
+ bne :+
+ lda #<SER_ERR_OVERFLOW
+ ldx #>SER_ERR_OVERFLOW
+ rts
+
+: ldy SendTail
+ sta SendBuf,y
+ inc SendTail
+ dec SendFreeCnt
+ lda #$FF ; TryHard = true
+ jsr TryToSend
+ lda #<SER_ERR_OK
+ tax
+ rts
;----------------------------------------------------------------------------
; STATUS: Return the status in the variable pointed to by ptr1.
; Must return an SER_ERR_xx code in a/x.
STATUS:
- lda ACIA_STATUS
- ldx #$00
- sta (ptr1,x)
- txa ; SER_ERR_OK
- rts
+ lda ACIA_STATUS
+ ldx #$00
+ sta (ptr1,x)
+ txa ; SER_ERR_OK
+ rts
;----------------------------------------------------------------------------
; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
; Must return an SER_ERR_xx code in a/x.
IOCTL:
- lda #<SER_ERR_INV_IOCTL
- ldx #>SER_ERR_INV_IOCTL
- rts
+ lda #<SER_ERR_INV_IOCTL
+ ldx #>SER_ERR_INV_IOCTL
+ rts
;----------------------------------------------------------------------------
; IRQ: Called from the builtin runtime IRQ handler as a subroutine. All
; was handled, otherwise with carry clear.
IRQ:
- ldx Index ; Check for open port
- beq Done
- lda ACIA_STATUS,x ; Check ACIA status for receive interrupt
- and #$08
- beq Done ; Jump if no ACIA interrupt
- lda ACIA_DATA,x ; Get byte from ACIA
- ldy RecvFreeCnt ; Check if we have free space left
- beq Flow ; Jump if no space in receive buffer
- ldy RecvTail ; Load buffer pointer
- sta RecvBuf,y ; Store received byte in buffer
- inc RecvTail ; Increment buffer pointer
- dec RecvFreeCnt ; Decrement free space counter
- ldy RecvFreeCnt ; Check for buffer space low
- cpy #33
- bcc Flow ; Assert flow control if buffer space low
- rts ; Interrupt handled (carry already set)
-
- ; Assert flow control if buffer space too low
-Flow: lda RtsOff
- sta ACIA_CMD,x
- sta Stopped
- sec ; Interrupt handled
-Done: rts
+ ldx Index ; Check for open port
+ beq Done
+ lda ACIA_STATUS,x ; Check ACIA status for receive interrupt
+ and #$08
+ beq Done ; Jump if no ACIA interrupt
+ lda ACIA_DATA,x ; Get byte from ACIA
+ ldy RecvFreeCnt ; Check if we have free space left
+ beq Flow ; Jump if no space in receive buffer
+ ldy RecvTail ; Load buffer pointer
+ sta RecvBuf,y ; Store received byte in buffer
+ inc RecvTail ; Increment buffer pointer
+ dec RecvFreeCnt ; Decrement free space counter
+ ldy RecvFreeCnt ; Check for buffer space low
+ cpy #33
+ bcc Flow ; Assert flow control if buffer space low
+ rts ; Interrupt handled (carry already set)
+
+ ; Assert flow control if buffer space too low
+Flow: lda RtsOff
+ sta ACIA_CMD,x
+ sta Stopped
+ sec ; Interrupt handled
+Done: rts
;----------------------------------------------------------------------------
; Try to send a byte. Internal routine. A = TryHard
TryToSend:
- sta tmp1 ; Remember tryHard flag
-Again: lda SendFreeCnt
- cmp #$FF
- beq Quit ; Bail out
-
- ; Check for flow stopped
- lda Stopped
- bne Quit ; Bail out
-
- ; Check that ACIA is ready to send
- lda ACIA_STATUS
- and #$10
- bne Send
- bit tmp1 ; Keep trying if must try hard
- bmi Again
-Quit: rts
-
- ; Send byte and try again
-Send: ldy SendHead
- lda SendBuf,y
- sta ACIA_DATA
- inc SendHead
- inc SendFreeCnt
- jmp Again
+ sta tmp1 ; Remember tryHard flag
+Again: lda SendFreeCnt
+ cmp #$FF
+ beq Quit ; Bail out
+
+ ; Check for flow stopped
+ lda Stopped
+ bne Quit ; Bail out
+
+ ; Check that ACIA is ready to send
+ lda ACIA_STATUS
+ and #$10
+ bne Send
+ bit tmp1 ; Keep trying if must try hard
+ bmi Again
+Quit: rts
+
+ ; Send byte and try again
+Send: ldy SendHead
+ lda SendBuf,y
+ sta ACIA_DATA
+ inc SendHead
+ inc SendFreeCnt
+ jmp Again