COMMLIB2 - ORDINARY RS232 TRANSFER LIBRARY - DOCUMENTATION ---------------------------------------------------------- Commlib2 is the second version of the RS232 communication package for C64. It is consisted of small routines to be used with standard User Port modem interfaces. In my experiments, the NMI handler worked flawlessly at 19200bps in 2MHz mode of C128 (C64 mode, using VDC screen) and 9600bps in 1MHz C64 while VIC screen is closed. But there are some troubles with modems. These speeds are error-free with a null-modem cable to a PC, in contrary to modems. A modem (if its echo is on) passes the input to the output line. This means that sending and receiving occur at the same time, which lacks the NMI handler routine. Thus, the solution with modems is closing the echo before switching to a high speed. By the way, this package allows 4800bps with VIC open. The definition of input buffer is flexible, this means you can use almost all of the internal memory as an input buffer. You should reserve large memory for the high baud rates, because the screen driver routine you have probably will not keep the data at high speeds. And, there is no output buffer implemented in this version. This is not a big problem for a monotasking application, also statistics says that the input/output transfer ratio is something 10/1 on the average for ordinary terminal usage. As for the flow control methods, you can select one of these: rts/cts, xon/xoff, both or none. But, the application is responsible for this control, using 'enable' and 'disable' calls. The library does not check the buffer overflows. Nevertheless, you won't need to use them for flow control. In my tests, the routines kept the data flawless even at 19200bps. With the 'vdc80col' screen driver I have achieved 1730+ cps (same test for desterm gave 760 cps at 9600bps). Here is the usage explanation of the commlib2 RS232 library: Memory Usage of This Version: ----------------------------- $ca00-$ca1f : jump table $ca20-$ccff : the code $cd00-$cd40 : settings and workspace $9000-$a000 : input buffer $ce00-$ceff : ascii-petscii table created by term routine You can change the buffer and ascii-table places easily, if you want. The Routines: ------------- Setup ($ca00) : Activates the NMI handler and sets the workspace Uninstall ($ca03) : Recovers the NMI handler, and deactivates the NMI Enable ($ca06) : Enables the incoming and outgoing data Disable ($ca09) : Suppresses the input (drops rts and/or sends xoff) Sendit ($ca0c) : Sending a byte through the rs232 channel getbyt ($ca0f) : Reads a byte from the rs232 receive buffer putbyt ($ca12) : Sending a byte safely (use it at 4800bps) Asciitable ($ca15) : Prepares an ascii-petscii translation table Term ($ca18) : Simple terminal program (ctrl-j exits) Speed ($ca1b) : DTE baud (speed) selection Location of the Routines' Code: ------------------------------- $ca20-$ca6e : setup $ca76-$ca90 : uninstall $ca91-$cab8 : disable $cab9-$caee : enable $caef-$cb21 : sendit $cb23-$cb30 : NMI detect $cb31-$cb64 : NMI sending part $cb65-$cb6d : restore key $cb6e-$cb83 : NMI startget $cb84-$cbd0 : NMI receiving part $cbd1-$cbe0 : safe send $cbe1-$cbf8 : speed select $cbf9-$cc3f : getbyt $cc40-$cc9c : ascii-table $cc9d-$ccff : terminal program Default Values of Workspace: ---------------------------- :cd00 00 10 90 80 10 10 00 90 :cd08 00 90 00 80 00 80 00 00 :cd10 00 00 00 00 00 00 97 01 :cd18 a2 01 00 00 c3 c7 02 00 Explanation of Workspace Settings: ---------------------------------- cd00 busy : 1 byte busy flag for sending process ($80=busy) cd01 timeout : 1 byte timeout in seconds (for putbyt (not implemented) ) cd02 inbuf : 1 byte pointer to input buffer start page cd03 outbuf : 1 byte pointer to output buffer (not implemented) cd04 inblen : 1 byte length of the input buffer in pages cd05 outblen : 1 byte length of the output buffer in pages cd06 inbsta : 2 bytes start index of input buffer cd08 inbend : 2 bytes end index of input buffer cd0a outbsta : 2 bytes start index of output buffer (not implemented) cd0c outbend : 2 bytes end index of output buffer (not implemented) cd0e inplock : 1 byte lock status for input ($80=locked) cd0f outlock : 1 byte lock status for output cd10 input : 1 byte byte read cd11 output : 1 byte byte sent cd12 tempinp : 1 byte temporary for read byte cd13 tempout : 1 byte temporary for sent byte cd14 inpidx : 1 byte bit counter while reading cd15 outidx : 1 byte bit counter while sending cd16 sbaud : 2 bytes time period for data bits while sending cd18 rbaud : 2 bytes time period for data bits while receiving cd1a flow : 1 byte flow control method ($80=rts/cts $40=xon/xoff) cd1b $01 : 1 byte ( value to write $dd00 ) cd1c $02 : 1 byte must not be changed ( mark bit ) cd1d $10 : 1 byte must not be changed ( space bit ) cd1e speed : 1 byte selected speed (default $02 = 2400bps) cd1f : 1 byte reserved for escape interpretation cd20 : 32 bytes timing values for various baud rates Flow Control Selection: ----------------------- method $cd1a -------- ----- none $00 Xon/Xoff $40 RTS/CTS $80 both $c0 Usages of the Routines: ----------------------- * Setup ($ca00) No preparation is required. Just call it to install the RS232 handler. Sets the necassary location in workspace (indexes and locks) jsr$ca00 * Uninstall ($ca03) A call to this routine deactivates the RS232 event handler. jsr$ca03 * Enable ($ca06) Sets NMI, sends xon and/or sets rts. So that getbyt and sendit routines work. Changes A register only. jsr$ca06 * Disable ($ca09) Waits the busy byte, sends xoff and/or drops rts, clears NMI. Use this, before accessing the serial ports (drive, printer, etc.) It changes only the A register. jsr$ca09 * Sendit ($ca0c) Waits the busy byte, and then sends the value in A register by 8N1 (8 data bits, no parity, 1 stop bit) X and Y registers remain the same. lda#$41 ; 'a' character jsr$ca0c lda#$54 ; 't' character jsr$ca0c lda#$0d ; carriage return jsr$ca0c ; now you can check the input buffer for 'OK' of modem. * Getbyt ($ca0f) Gets one byte from the input buffer. If the buffer is empty sets the carry flag. X and Y registers remain unchanged upon return. -jsr$ca0f bcs - jsr$ffd2 * sendsafe ($ca12) Secure version of sendit, meaning that it avoids bad raster lines while sending a byte. To be used with 4800bps while VIC is open. It has the same usage with the sendit routine. * Asciitable ($ca15) Puts the ascii-petscii conversion table to the memory page given by the A register. The table is simple and works only for tty emulation. lda#$ce jsr$ca15 * Term ($ca18) This is a little terminal program for testing the routines. It setups the handler and begins to scan keyboard and rs232. To exit from the term, either press ctrl-j or press the joystick button of port 2. Upon exiting, it uninstalls the handler. jsr$ca18 * Speed ($ca1b) Selects the DTE baud rate. To use it put the speed index into A register and then call the routine: lda#$02 ; 2400bps jsr$ca1b Use this table of speed index: A : 00 : 01 : 02 : 03 : 04 : 05 : 06 : 07 : Baud : 300 : 1200 : 2400 : 4800 : 7200 : 9600 : 14400 : 19200 : Speed (baud) Selection Manually: -------------------------------- If you have troubles with the default values, you can fiddle with them. The following table shows the default values: bps $cd16 $cd17 $cd18 $cd19 table location ----- ----- ----- ----- ----- -------------- 300 $00 $0d $10 $0d $cd20 1200 $34 $03 $42 $03 $cd24 2400 $97 $01 $a2 $01 $cd28 4800*** $c7 $00 $d0 $00 $cd2c 7200** $85 $00 $89 $00 $cd30 9600** $60 $00 $65 $00 $cd34 14400* $41 $00 $43 $00 $cd38 19200* $2f $00 $32 $00 $cd3c * : at 2mhz mode of C128 only, I don't know if it works with SCPU. ** : While VIC is closed. It works flawlessly for me. ***: While VIC is open, use 'sendsafe' routine instead of 'sendit'. + : If you encounter a problem with modem, try turning the echo off: ATE0 ++ : Don't forget to use 'sendsafe' at 4800 if VIC is open. Also note that, at the speeds 4800bps and higher, $ffd2 routine lacks. Use another screen manager for full speed (such as 'vdc80col', 'flicktty' or D.Dallman's fast 80 column program). By the way, keep the input buffer always big (default is 4096bytes) to keep up the data at higher rates. Information on Used Hardware Registers: --------------------------------------- $dd00-2 : out $dd01-0 : in $dd01-1 : rts $dd01-6 : cts $dd04,$dd05 : timer A (for sending) $dd06,$dd07 : timer B (for receiving) $dd0d : NMI $dd0e : timer control A $dd0f : timer control B Manual to Use This Package in Basic: ------------------------------------ . Setup Basic memory: poke56,144:clr . Running simple terminal program: sys51736 pressing ctrl-j exits . Setting up the RS232 routines: sys51712 . Closing the RS232 routines: sys51715 . Sending a byte: poke780,byte:sys51724 . Sending a byte slowly and safely: poke780,byte:sys51730 . reading a byte: sys51727:if(peek(783)and1)=0thenbyte=peek(780) note that, (peek(783)and1) equals to 1 means that there is no data. Example Terminal Program in Basic: ---------------------------------- 10 poke56,144:clr:sys51712 20 dim ge,se,a,c,a$,f$ 30 ge=51727:se=51724:a=780:c=783:f$=chr$(133) 40 sysge:if(peek(c)and1)=0 then print chr$(peek(a));:goto40 50 geta$:ifa$<>""thenpokea,asc(a$):sysse 60 if a$<>f$ goto 40 70 sys51715:end to quit from the program, press F1 to speed it up a little bit, change the line 40 like this: 40 sysge:if(peek(c)and1)=0then?chr$(peek(a));:sysge:?chr$(peek(a));:goto40 7200bps and 14400bps for IBM PC : --------------------------------- The two little .com programs below, switches the selected speed to either 7200bps or 14400bps. For instance, first select 9600bps: C:\> mode 96,n,8,1,n Then, switch to 14400bps: C:\> 14400 begin 644 7200.com ;NOL#[`R`[DI*L`#N2K`0[KK[`^PD?^[-(`29 ` end begin 644 14400.com ;NOL#[`R`[DI*L`#N2K`([KK[`^PD?^[-(`29 ` end Some Benchmarks: ---------------- BAUD Clock Screen cps ----- ----- ------ ---- 4800 1MHz VDC 472 9600 1MHz VDC 890 9600 2MHz VDC 930 14400 2MHz VDC 1375 19200 2MHz VDC 1730 These are rough results. I timed the 10548 bytes (206 lines) file with a stop-watch. NMI Handler (the heart of the library): --------------------------------------- ., cb22 78 sei ., cb23 48 pha ., cb24 ad 0d dd lda $dd0d ; read the NMI status ., cb27 10 3c bpl $cb65 ; branch if it's Restore key ., cb29 29 03 and #$03 ; check if it's timer NMI ., cb2b f0 41 beq $cb6e ; if not it is the start bit ., cb2d 29 02 and #$02 ; check for timer B (receive) ., cb2f d0 53 bne $cb84 ; branch to 'getbit' routine ., cb31 ad 1b cd lda $cd1b ; it was sending timer NMI ., cb34 8d 00 dd sta $dd00 ; put the bit, which is in the queue ., cb37 ad 1d cd lda $cd1d ; load the space bit ., cb3a 4e 13 cd lsr $cd13 ; get the next bit to send ., cb3d b0 03 bcs $cb42 ; check whether it's 0 or 1 ., cb3f ad 1c cd lda $cd1c ; load the mark bit for 0 ., cb42 8d 1b cd sta $cd1b ; put this value to queue byte ., cb45 ce 15 cd dec $cd15 ; decrease the bit index ., cb48 30 0c bmi $cb56 ; branch if the stop bit is sent ., cb4a f0 02 beq $cb4e ; branch for preparing the stop bit ., cb4c 68 pla ; return from the NMI ., cb4d 40 rti ., cb4e ad 1d cd lda $cd1d ; load the space bit ., cb51 8d 1b cd sta $cd1b ; put it to the queue ., cb54 68 pla ., cb55 40 rti ., cb56 a9 10 lda #$10 ; stop the timer-A ., cb58 8d 0e dd sta $dd0e ., cb5b a9 01 lda #$01 ; stop the timer-A NMI ., cb5d 8d 0d dd sta $dd0d ., cb60 8d 00 cd sta $cd00 ; clear the busy bit ., cb63 68 pla ., cb64 40 rti ., cb65 8a txa ; this passes the NMI control to the ., cb66 48 pha ; original NMI handler. ., cb67 98 tya ; since we detect the Restore key. ., cb68 48 pha ., cb69 a0 00 ldy #$00 ., cb6b 4c 56 fe jmp $fe56 ; Jump to the Kernal NMI handler ., cb6e a9 11 lda #$11 ; start the timer-B, we detected the RX ., cb70 8d 0f dd sta $dd0f ., cb73 a9 10 lda #$10 ; stop the flag2 NMI ., cb75 8d 0d dd sta $dd0d ., cb78 a9 82 lda #$82 ; start the timer-B NMI ., cb7a 8d 0d dd sta $dd0d ., cb7d a9 08 lda #$08 ; put the number of bits to be received, ., cb7f 8d 14 cd sta $cd14 ; to the recieve index byte. ., cb82 68 pla ., cb83 40 rti ., cb84 ad 01 dd lda $dd01 ; read the RX line (this is timer-B NMI) ., cb87 4a lsr a ; put into carry flag ., cb88 6e 12 cd ror $cd12 ; put the carry flag into receive byte. ., cb8b ce 14 cd dec $cd14 ; decrese the bit number index ., cb8e f0 02 beq $cb92 ; if it's last bit branch. ., cb90 68 pla ., cb91 40 rti ., cb92 a9 02 lda #$02 ; stop the timer-B ., cb94 8d 0d dd sta $dd0d ., cb97 a9 10 lda #$10 ; stop the timer-B NMI ., cb99 8d 0f dd sta $dd0f ... ... ; then place the read byte into the ... ; receive buffer. Possible Optimizations: ----------------------- . Use the NMI directly. That is close the ROMs ( #$35 -> $01 ), and direct the NMI to $cb22 ( #$22 -> $fffa , #$cb -> $fffb ) . If the distortion of the restore key is not important, remove the 'bpl' command at $cb27 and rearrange the handler . You can use the zero-page variables. Try to move the workspace somewhere in the zero-page. As a last note I can say that this library is written for the programmers, who want to write their own serial-communication oriented application. This library is not an end-product so. That's all for this commlib2 library. Make use of it... Ilker Ficicilar filker@newton.physics.metu.edu.tr c068214@orca.cc.metu.edu.tr http://www.physics.metu.edu.tr/~filker/cbm.html ---