;.lib "axm" ; ;begin ;title "A2232 serial board driver" ; ;set modules "2232" ;set executable "2232.bin" ; ;;;;set nolink ; ;set temporary directory "t:" ; ;set assembly options "-m6502 -l60:t:list" ;set link options "bin"; loadadr" ;;;bin2c 2232.bin msc6502.h msc6502code ;end ; ; ; ### Commodore A2232 serial board driver for NetBSD by JM v1.3 ### ; ; - Created 950501 by JM - ; ; ; Serial board driver software. ; ; % Copyright (c) 1995 Jukka Marin <jmarin@jmp.fi>. % 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, and the entire permission notice in its entirety, % including the disclaimer of warranties. % 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. The name of the author may not be used to endorse or promote % products derived from this software without specific prior % written permission. % % ALTERNATIVELY, this product may be distributed under the terms of % the GNU General Public License, in which case the provisions of the % GPL are required INSTEAD OF the above restrictions. (This clause is % necessary due to a potential bad interaction between the GPL and % the restrictions contained in a BSD-style copyright.) % % THIS SOFTWARE IS PROVIDED `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 AUTHOR 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. ; ; ; Bugs: ; ; - Can't send a break yet ; ; ; ; Edited: ; ; - 950501 by JM -> v0.1 - Created this file. ; - 951029 by JM -> v1.3 - Carrier Detect events now queued in a separate ; queue. ; ; CODE equ $3800 ; start address for program code CTL_CHAR equ $00 ; byte in ibuf is a character CTL_EVENT equ $01 ; byte in ibuf is an event EVENT_BREAK equ $01 EVENT_CDON equ $02 EVENT_CDOFF equ $03 EVENT_SYNC equ $04 XON equ $11 XOFF equ $13 VARBASE macro *starting_address ; was VARINIT _varbase set \1 endm VARDEF macro *name space_needs \1 equ _varbase _varbase set _varbase+\2 endm stz macro * address db $64,\1 endm stzax macro * address db $9e,<\1,>\1 endm biti macro * immediate value db $89,\1 endm smb0 macro * address db $87,\1 endm smb1 macro * address db $97,\1 endm smb2 macro * address db $a7,\1 endm smb3 macro * address db $b7,\1 endm smb4 macro * address db $c7,\1 endm smb5 macro * address db $d7,\1 endm smb6 macro * address db $e7,\1 endm smb7 macro * address db $f7,\1 endm ;-----------------------------------------------------------------------; ; ; ; stuff common for all ports, non-critical (run once / loop) ; ; ; DO_SLOW macro * port_number ; .local ; ; lda CIA+C_PA ; check all CD inputs ; cmp CommonCDo ; changed from previous accptd? ; beq =over ; nope, do nothing else here ; ; ; cmp CommonCDb ; bouncing? ; beq =nobounce ; nope -> ; ; ; sta CommonCDb ; save current state ; lda #64 ; reinitialize counter ; sta CommonCDc ; ; jmp =over ; skip CD save ; ; ; =nobounce dec CommonCDc ; no, decrement bounce counter ; bpl =over ; not done yet, so skip CD save ; ; ; =saveCD ldx CDHead ; get write index ; sta cdbuf,x ; save status in buffer ; inx ; ; cpx CDTail ; buffer full? ; .if ne ; no: preserve status: ; stx CDHead ; update index in RAM ; sta CommonCDo ; save state for the next check ; .end ; ; =over .end local ; endm ; ; ;-----------------------------------------------------------------------; ; port specific stuff (no data transfer) DO_PORT macro * port_number .local ; ; lda SetUp\1 ; reconfiguration request? ; .if ne ; yes: ; lda SoftFlow\1 ; get XON/XOFF flag ; sta XonOff\1 ; save it ; lda Param\1 ; get parameter ; ora #%00010000 ; use baud generator for Rx ; sta ACIA\1+A_CTRL ; store in control register ; stz OutDisable\1 ; enable transmit output ; stz SetUp\1 ; no reconfiguration no more ; .end ; ; ; ; lda InHead\1 ; get write index ; sbc InTail\1 ; buffer full soon? ; cmp #200 ; 200 chars or more in buffer? ; lda Command\1 ; get Command reg value ; and #%11110011 ; turn RTS OFF by default ; .if cc ; still room in buffer: ; ora #%00001000 ; turn RTS ON ; .end ; ; sta ACIA\1+A_CMD ; set/clear RTS ; ; ; lda OutFlush\1 ; request to flush output buffer; .if ne ; yessh! ; lda OutHead\1 ; get head ; sta OutTail\1 ; save as tail ; stz OutDisable\1 ; enable transmit output ; stz OutFlush\1 ; clear request ; .end .end local endm DO_DATA macro * port number .local lda ACIA\1+A_SR ; read ACIA status register ; biti [1<<3] ; something received? ; .if ne ; yes: ; biti [1<<1] ; framing error? ; .if ne ; yes: ; lda ACIA\1+A_DATA ; read received character ; bne =SEND ; not break -> ignore it ; ldx InHead\1 ; get write pointer ; lda #CTL_EVENT ; get type of byte ; sta ictl\1,x ; save it in InCtl buffer ; lda #EVENT_BREAK ; event code ; sta ibuf\1,x ; save it as well ; inx ; ; cpx InTail\1 ; still room in buffer? ; .if ne ; absolutely: ; stx InHead\1 ; update index in memory ; .end ; ; jmp =SEND ; go check if anything to send ; .end ; ; ; normal char received: ; ldx InHead\1 ; get write index ; lda ACIA\1+A_DATA ; read received character ; sta ibuf\1,x ; save char in buffer ; stzax ictl\1 ; set type to CTL_CHAR ; inx ; ; cpx InTail\1 ; buffer full? ; .if ne ; no: preserve character: ; stx InHead\1 ; update index in RAM ; .end ; ; and #$7f ; mask off parity if any ; cmp #XOFF ; XOFF from remote host? ; .if eq ; yes: ; lda XonOff\1 ; if XON/XOFF handshaking.. ; sta OutDisable\1 ; ..disable transmitter ; .end ; ; .end ; ; ; ; ; BUFFER FULL CHECK WAS HERE ; ; ; =SEND lda ACIA\1+A_SR ; transmit register empty? ; and #[1<<4] ; ; .if ne ; yes: ; ldx OutCtrl\1 ; sending out XON/XOFF? ; .if ne ; yes: ; lda CIA+C_PB ; check CTS signal ; and #[1<<\1] ; (for this port only) ; bne =DONE ; not allowed to send -> done ; stx ACIA\1+A_DATA ; transmit control char ; stz OutCtrl\1 ; clear flag ; jmp =DONE ; and we're done ; .end ; ; ; ; ldx OutTail\1 ; anything to transmit? ; cpx OutHead\1 ; ; .if ne ; yes: ; lda OutDisable\1 ; allowed to transmit? ; .if eq ; yes: ; lda CIA+C_PB ; check CTS signal ; and #[1<<\1] ; (for this port only) ; bne =DONE ; not allowed to send -> done ; lda obuf\1,x ; get a char from buffer ; sta ACIA\1+A_DATA ; send it away ; inc OutTail\1 ; update read index ; .end ; ; .end ; ; .end ; ; =DONE .end local endm PORTVAR macro * port number VARDEF InHead\1 1 VARDEF InTail\1 1 VARDEF OutDisable\1 1 VARDEF OutHead\1 1 VARDEF OutTail\1 1 VARDEF OutCtrl\1 1 VARDEF OutFlush\1 1 VARDEF SetUp\1 1 VARDEF Param\1 1 VARDEF Command\1 1 VARDEF SoftFlow\1 1 ; private: VARDEF XonOff\1 1 endm VARBASE 0 ; start variables at address $0000 PORTVAR 0 ; define variables for port 0 PORTVAR 1 ; define variables for port 1 PORTVAR 2 ; define variables for port 2 PORTVAR 3 ; define variables for port 3 PORTVAR 4 ; define variables for port 4 PORTVAR 5 ; define variables for port 5 PORTVAR 6 ; define variables for port 6 VARDEF Crystal 1 ; 0 = unknown, 1 = normal, 2 = turbo VARDEF Pad_a 1 VARDEF TimerH 1 VARDEF TimerL 1 VARDEF CDHead 1 VARDEF CDTail 1 VARDEF CDStatus 1 VARDEF Pad_b 1 VARDEF CommonCDo 1 ; for carrier detect optimization VARDEF CommonCDc 1 ; for carrier detect debouncing VARDEF CommonCDb 1 ; for carrier detect debouncing VARBASE $0200 VARDEF obuf0 256 ; output data (characters only) VARDEF obuf1 256 VARDEF obuf2 256 VARDEF obuf3 256 VARDEF obuf4 256 VARDEF obuf5 256 VARDEF obuf6 256 VARDEF ibuf0 256 ; input data (characters, events etc - see ictl) VARDEF ibuf1 256 VARDEF ibuf2 256 VARDEF ibuf3 256 VARDEF ibuf4 256 VARDEF ibuf5 256 VARDEF ibuf6 256 VARDEF ictl0 256 ; input control information (type of data in ibuf) VARDEF ictl1 256 VARDEF ictl2 256 VARDEF ictl3 256 VARDEF ictl4 256 VARDEF ictl5 256 VARDEF ictl6 256 VARDEF cdbuf 256 ; CD event queue ACIA0 equ $4400 ACIA1 equ $4c00 ACIA2 equ $5400 ACIA3 equ $5c00 ACIA4 equ $6400 ACIA5 equ $6c00 ACIA6 equ $7400 A_DATA equ $00 A_SR equ $02 A_CMD equ $04 A_CTRL equ $06 ; 00 write transmit data read received data ; 02 reset ACIA read status register ; 04 write command register read command register ; 06 write control register read control register CIA equ $7c00 ; 8520 CIA C_PA equ $00 ; port A data register C_PB equ $02 ; port B data register C_DDRA equ $04 ; data direction register for port A C_DDRB equ $06 ; data direction register for port B C_TAL equ $08 ; timer A C_TAH equ $0a C_TBL equ $0c ; timer B C_TBH equ $0e C_TODL equ $10 ; TOD LSB C_TODM equ $12 ; TOD middle byte C_TODH equ $14 ; TOD MSB C_DATA equ $18 ; serial data register C_INTCTRL equ $1a ; interrupt control register C_CTRLA equ $1c ; control register A C_CTRLB equ $1e ; control register B section main,code,CODE-2 db >CODE,<CODE ;-----------------------------------------------------------------------; ; here's the initialization code: ; ; ; R_RESET ldx #$ff ; txs ; initialize stack pointer ; cld ; in case a 6502 is used... ; ldx #0 ; ; lda #0 ; ; ldy #Crystal ; this many bytes to clear ; clr_loop sta 0,x ; clear zero page variables ; inx ; ; dey ; ; bne clr_loop ; ; ; ; stz CommonCDo ; force CD test at boot ; stz CommonCDb ; ; stz CDHead ; clear queue ; stz CDTail ; ; ; ; lda #0 ; ; sta Pad_a ; ; lda #170 ; test cmp ; cmp #100 ; ; .if cs ; ; inc Pad_a ; C was set ; .end ; ; ; ;-----------------------------------------------------------------------; ; Speed check ; ;-----------------------------------------------------------------------; ; lda Crystal ; speed already set? ; beq DoSpeedy ; ; jmp LOOP ; yes, skip speed test ; ; ; DoSpeedy lda #%10011000 ; 8N1, 1200/2400 bps ; sta ACIA0+A_CTRL ; ; lda #%00001011 ; enable DTR ; sta ACIA0+A_CMD ; ; lda ACIA0+A_SR ; read status register ; ; ; lda #%10000000 ; disable all ints (unnecessary); sta CIA+C_INTCTRL ; ; lda #255 ; program the timer ; sta CIA+C_TAL ; ; sta CIA+C_TAH ; ; ; ; ldx #0 ; ; stx ACIA0+A_DATA ; transmit a zero ; nop ; ; nop ; ; lda ACIA0+A_SR ; read status ; nop ; ; nop ; ; stx ACIA0+A_DATA ; transmit a zero ; Speedy1 lda ACIA0+A_SR ; read status ; and #[1<<4] ; transmit data reg empty? ; beq Speedy1 ; not yet, wait more ; ; ; lda #%00010001 ; load & start the timer ; stx ACIA0+A_DATA ; transmit one more zero ; sta CIA+C_CTRLA ; ; Speedy2 lda ACIA0+A_SR ; read status ; and #[1<<4] ; transmit data reg empty? ; beq Speedy2 ; not yet, wait more ; stx CIA+C_CTRLA ; stop the timer ; ; ; lda CIA+C_TAL ; copy timer value for 68k ; sta TimerL ; ; lda CIA+C_TAH ; ; sta TimerH ; ; cmp #$d0 ; turbo or normal? ; .if cs ; ; lda #2 ; turbo! :-) ; .else ; ; lda #1 ; normal :-( ; .end ; ; sta Crystal ; ; lda #0 ; ; sta ACIA0+A_SR ; ; sta ACIA0+A_CTRL ; reset UART ; sta ACIA0+A_CMD ; ; ; jmp LOOP ; ; ; ; ;-----------------------------------------------------------------------; ; ; ; The Real Thing: ; ; ; LOOP DO_SLOW ; do non-critical things ; jsr do_input ; check for received data DO_PORT 0 jsr do_input DO_PORT 1 jsr do_input DO_PORT 2 jsr do_input DO_PORT 3 jsr do_input DO_PORT 4 jsr do_input DO_PORT 5 jsr do_input DO_PORT 6 jsr do_input jmp LOOP do_input DO_DATA 0 DO_DATA 1 DO_DATA 2 DO_DATA 3 DO_DATA 4 DO_DATA 5 DO_DATA 6 rts ;-----------------------------------------------------------------------; section vectors,data,$3ffa dw $d0d0 dw R_RESET dw $c0ce ;-----------------------------------------------------------------------; end