Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Fast RS-232 routines for C128 wanted

13 views
Skip to first unread message

Christian Johansson

unread,
Feb 26, 2004, 4:28:55 PM2/26/04
to
Hi!

With the Kernal routines of the C128, the maximum achievable baud rate for
the User Port is 2400 bps when running at 2 MHz. However, Desterm can run at
9600 bps so I know it is possible to achieve higher than 2400 bps by writing
custom Kernal routines. Does anybody have such routines? There has been a
routine from Transactor posted in this group earlier to reach 2400 bps on
the C64 (with the Kernal routines, only 1200 bps can be reached on the C64).
I guess that I would be able to modify that routine so I could run at 4800
bps on the C128 in 2 MHz mode but since that is some job, I would appreciate
if somebody already has a fast RS-232 routine for the C128. I know that it
is possible to reach even higher than 9600 bps by not using NMIs and instead
read from/write to the pins directly. Over5 does this. However, doing that
is _extremely_ time critical and therefore hard to get working so I'm more
after an NMI driven solution that just replaces the RS-232 Kernal routines
with optimized routines that make it possible to reach 4800 bps on the C128
in 2 MHz.

Best Regards,

Christian Johansson


White Flame (aka David Holz)

unread,
Feb 26, 2004, 8:06:30 PM2/26/04
to
"Christian Johansson" <c...@combort.se> wrote in message
news:ryt%b.50503$mU6.2...@newsb.telia.net...

> I know that it
> is possible to reach even higher than 9600 bps by not using NMIs and
instead
> read from/write to the pins directly. Over5 does this. However, doing that
> is _extremely_ time critical and therefore hard to get working so I'm more

Actually, it isn't hard to get working at all. I got 56kbps working on my
C64<->PC using an Over5-ish routine. However, it requires the screen to be
blanked. Do you need the screen visible (term program, etc) or can it be
blanked (file transfer, etc)?

> after an NMI driven solution that just replaces the RS-232 Kernal routines
> with optimized routines that make it possible to reach 4800 bps on the
C128
> in 2 MHz.

You could try opening up a kernal RS232 channel, look at the RAM addresses
that hold the CIA frequencies for RS232, and change them to clock/4800.

--
White Flame (aka David Holz)
http://www.white-flame.com/
(spamblock in effect)


Christian Johansson

unread,
Feb 27, 2004, 5:15:00 PM2/27/04
to
"White Flame (aka David Holz)" <whitef...@y.a.h.o.o.com> skrev i
meddelandet news:c1m48i$1j9s$1...@barad-dur.nas.com...

I'm thinking about using the following code from Transactor , Vol 9, Issue
3. As you can see, it contains the following definitions for 300, 1200 and
2400 bps:

; Start bit times.
;
strt24 .word 459 ; 2400
strt12 .word 1090 ; 1200
strt03 .word 4915 ; 300
;
; Full bit times.
;
full24 .word 421 ; 2400
full12 .word 845 ; 1200
full03 .word 3410 ; 300

What I'm wondering is what the values for 4800 should be (4800 should be
achievable with this routine when running at 2 MHz I think). I just can't
figure out how the values above have been calculated so that I can think out
what they should be for 4800. I wish I had access to the text in the article
but unfortunately nobody has scanned that Transactor article yet.


--------------------------------------------------------------
;
; NEWMODEM.SRC
;
; Routines by George Hug
; Transactor, Vol 9, Issue 3
;
; Modified 9-26-90 by Fuzzy Fox to
; allow 7 bits plus parity to be
; sent. Multiple stops bits are
; still not supported.
;
;-----------------------------------
;
; Set the following flag to 1
; for C128 mode.
;
c128 = 0
;
.ifn c128 ; C128 mode
;
ctrlreg1 = $0a10
ctrlreg2 = $0a11
ribuf = $c8
robuf = $ca
baudof = $0a16
ridbe = $0a18
ridbs = $0a19
rodbs = $0a1a
rodbe = $0a1b
enabl = $0a0f
rstkey = $fa4b
norest = $fa5f
return = $ff33
oldout = $ef79
oldchk = $f10e
ochrin = $ef06
ogetin = $eeeb
findfn = $f202
devnum = $f212
nofile = $f682
;
*= $1900 ; Start addr
;
.else ; C64 mode
;
ctrlreg1 = $0293
ctrlreg2 = $0294
ribuf = $f7
robuf = $f9
baudof = $0299
ridbe = $029b
ridbs = $029c
rodbs = $029d
rodbe = $029e
enabl = $02a1
rstkey = $fe56
norest = $fe72
return = $febc
oldout = $f1ca
oldchk = $f21b
ochrin = $f157
ogetin = $f13e
findfn = $f30f
devnum = $f31f
nofile = $f701
;
*= $cd00
;
.endif
;
;-----------------------------------
;
; Jump table.
;
xx00 jmp setup
xx03 jmp inable
xx06 jmp disabl
xx09 jmp rsget
xx0c jmp rsout
;
; Start bit times.
;
strt24 .word 459 ; 2400
strt12 .word 1090 ; 1200
strt03 .word 4915 ; 300
;
; Full bit times.
;
full24 .word 421 ; 2400
full12 .word 845 ; 1200
full03 .word 3410 ; 300
;
;-----------------------------------
;
setup
.ifn c128 ; C128 mode.
lda #<nmi128
ldy #>nmi128
.else ; C64 mode.
lda #<nmi64
ldy #>nmi64
.endif
sta $0318
sty $0319
lda #<nchkin
ldy #>nchkin
sta $031e
sty $031f
lda #<nbsout
ldy #>nbsout
sta $0326
sty $0327
lda #<nchrin
ldy #>nchrin
sta $0324
sty $0325
lda #<ngetin
ldy #>ngetin
sta $032a
sty $032b
rts
;
;-----------------------------------
;
.ife c128 ; C64 mode.
;
nmi64 pha ; Must save registers
txa ; on the C64.
pha
tya
pha
.endif
;
nmi128 cld
ldx $dd07 ; Sample timer B high byte.
lda #$7f ; Disable CIA NMI's.
sta $dd0d
lda $dd0d ; Read/clear flags.
bpl notcia ; (Restore key).
cpx $dd07 ; Timer B timeout?
ldy $dd01 ; Sample pin C.
bcs mask ; No Timer B timout.
ora #$02 ; Yes, set flag in Acc.
ora $dd0d ; Read/clear flags again.
mask and enabl ; Mask out non-enabled.
tax ; These must be serviced.
lsr a ; Timer A? (Bit 0).
bcc ckflag ; No
lda $dd00 ; Yes, put bit on pin M.
and #$fb
ora $b5
sta $dd00
ckflag txa
and #$10 ; Flag NMI? (Bit 4).
beq nmion ; No.
strtlo lda #$42 ; Yes, Start bit to Timer B.
sta $dd06
strthi lda #$04 ; These values are self-modifying.
sta $dd07
lda #$11 ; Start Timer B counting.
sta $dd0f
lda #$12 ; Flag NMI off, Timer B on.
eor enabl ; Update mask.
sta enabl
sta $dd0d ; Enable new configuration.
fulllo lda #$4d ; Change reload latch.
sta $dd06 ; to full-bit time.
fullhi lda #$03 ; These values are self-modifying.
sta $dd07
lda #$08 ; Number of bits to receive.
sta $a8
bne chktxd ; Branch always.
notcia ldy #$00
jmp rstkey ; or JMP NOREST to disable Restore.
nmion lda enabl ; Re-enable NMI's.
sta $dd0d
txa
and #$02 ; Timer B? (Bit 1).
beq chktxd ; No.
tya ; Yes, sampled from way back when.
lsr a
ror $aa ; RS232 is LSB first.
dec $a8 ; Byte finished?
bne txd ; No.
ldy ridbe ; Yes, put byte to buffer.
lda $aa
sta (ribuf),y ; (No over-run test!)
inc ridbe
lda #$00 ; Stop Timer B.
sta $dd0f
lda #$12 ; Timer B NMI off, Flag on.
switch ldy #$7f ; Disable NMI's.
sty $dd0d
sty $dd0d ; Twice.
eor enabl ; Update mask.
sta enabl
sta $dd0d ; Enable new config.
txd txa
lsr a ; Timer A?
chktxd bcc exit ; No.
dec $b4 ; Yes, byte finished?
bmi char ; Yes.
lda #$04 ; No, prep next bit.
ror $b6 ; (Fill with stop bits).
bcs store
low lda #$00
store sta $b5
exit jmp return ; Restore regs, RTI.
char ldy rodbs
cpy rodbe ; Buffer empty?
beq txoff ; Yes.
getbuf lda (robuf),y ; No, prep next byte.
inc rodbs
sta $b6
lda #$09 ; Number of bits to send.
sta $b4
bne low ; Always - do start bit.
txoff ldx #$00 ; Stop Timer A.
stx $dd0e
lda #$01 ; Disable Timer A NMI.
bne switch ; Always.
;
;-----------------------------------
;
disabl pha ; Turns off modem port.
test lda enabl
and #$03 ; Any current activity?
bne test ; Yes, test again.
lda #$10 ; No, disable Flag NMI.
sta $dd0d
lda #$02
and enabl ; Currently receiving?
bne test ; Yes, start over.
sta enabl ; All off, update mask.
pla
rts
;
;-----------------------------------
;
nbsout pha ; New BSOUT.
lda $9a
cmp #$02
bne notmod
pla
rsout sta $9e ; Output to modem.
sty $97
; ---- Patch by Fox ----
lda #$20
bit ctrlreg2 ; Parity being generated?
beq point ; No, so send the full 8 bits.
php ; Save flag settings.
lda $9e
and #$7f ; Set byte to 7 bits.
tay
plp ; Restore flags.
bmi 10$ ; Mark/Space parity.
lda parity,y
10$ bvs 90$
eor #$80 ; Flip parity if wrong sense.
90$ sta $9e ; ---- End Patch ----
point ldy rodbe
lda $9e
sta (robuf),y ; Not official yet.
iny
cpy rodbs ; Buffer full?
beq fulbuf ; Yes.
sty rodbe ; No, bump pointer.
strtup lda enabl
and #$01 ; Transmitting now?
bne ret3 ; Yes.
sta $b5 ; No, prep start bit.
lda #$09
sta $b4 ; Number of bits to send.
ldy rodbs
lda (robuf),y
sta $b6 ; and next byte.
inc rodbs
lda baudof ; Full Tx bit time to Timer A.
sta $dd04
lda baudof+1
sta $dd05
lda #$11 ; Start Timer A.
sta $dd0e
lda #$81 ; Enable Timer A NMI.
change sta $dd0d ; NMI clears flag if set.
php ; Save IRQ status.
sei ; Disable IRQ's.
ldy #$7f ; Disable NMI's.
sty $dd0d
sty $dd0d ; Twice.
ora enabl ; Update mask.
sta enabl
sta $dd0d ; Enable new configuration.
plp ; Restore IRQ status.
ret3 clc
ldy $97
lda $9e
rts
fulbuf jsr strtup
jmp point
notmod pla ; Back to old BSOUT.
jmp oldout
;
;-----------------------------------
;
nchkin jsr findfn ; New CHKIN.
bne nosuch
jsr devnum
lda $ba
cmp #$02
bne back
sta $99
inable sta $9e ; Enable RS232 input.
sty $97
baud lda baudof+1 ; Set receive to same
and #$06 ; baud rate as xmit.
tay
lda strt24,y
sta strtlo+1 ; Self-mod
lda strt24+1,y
sta strthi+1
lda full24,y
sta fulllo+1
lda full24+1,y
sta fullhi+1
lda enabl
and #$12 ; Flag or Timer B on?
bne ret1 ; Yes.
sta $dd0f ; No, stop Timer B.
lda #$90 ; Turn on Flag NMI.
jmp change
nosuch jmp nofile
back lda $ba
jmp oldchk
;
;-----------------------------------
;
nchrin lda $99 ; New CHRIN.
cmp #$02
beq rsget
jmp ochrin
ngetin lda $99 ; New GETIN.
cmp #$02
beq rsget
jmp ogetin
;
rsget sta $9e ; Input from modem.
sty $97
ldy ridbs
cpy ridbe ; Buffer empty?
beq ret2 ; Yes.
lda (ribuf),y ; No, fetch character.
sta $9e
; ---- Patch by Fox ----
ldy ctrlreg2 ; Parity expected?
cpy #$20
bcc 10$ ; No, so keep all 8 bits.
and #$7f ; Otherwise reduce to 7 bits.
sta $9e
10$ ; ---- End Patch ----
inc ridbs
ret1 clc ; CC = char in acc.
ret2 ldy $97
lda $9e
last rts ; CS = buffer empty.
;
;-----------------------------------
;
; This table looks up the odd parity value for a byte between 0-7F.
; If the parity to be sent is even parity, the high bit is simply
; toggled.
;
parity .byte $00,$81,$82,$03,$84,$05,$06,$87
.byte $88,$09,$0a,$8b,$0c,$8d,$8e,$0f
.byte $90,$11,$12,$93,$14,$95,$96,$17
.byte $18,$99,$9a,$1b,$9c,$1d,$1e,$9f
.byte $a0,$21,$22,$a3,$24,$a5,$a6,$27
.byte $28,$a9,$aa,$2b,$ac,$2d,$2e,$af
.byte $30,$b1,$b2,$33,$b4,$35,$36,$b7
.byte $b8,$39,$3a,$bb,$3c,$bd,$be,$3f
.byte $c0,$41,$42,$c3,$44,$c5,$c6,$47
.byte $48,$c9,$ca,$4b,$cc,$4d,$4e,$cf
.byte $50,$d1,$d2,$53,$d4,$55,$56,$d7
.byte $d8,$59,$5a,$db,$5c,$dd,$de,$5f
.byte $60,$e1,$e2,$63,$e4,$65,$66,$e7
.byte $e8,$69,$6a,$eb,$6c,$ed,$ee,$6f
.byte $f0,$71,$72,$f3,$74,$f5,$f6,$77
.byte $78,$f9,$fa,$7b,$fc,$7d,$7e,$ff
;
.end


Anton Treuenfels

unread,
Mar 1, 2004, 10:56:16 PM3/1/04
to
Oh, I love that table lookup for parity. I've often thought that it would be
better to calculate any parity bit before stuffing the byte in the output
buffer. Saves a whole bunch of time that shouldn't be wasted during the
interrupt itself.

One question, though. In the listing the table is marked as "odd parity",
but there are actually an even number of bits in each table byte. Shouldn't
this therefore be an "even parity" table?

As for the timings...IIRC, Hug didn't bother checking for a false start bit
(he said it couldn't happen anyway), so the start timing simply skipped to
the first data bit. It looks like he wanted to reach the beginning of the
"valid data" time rather than the middle (so as to allow for drift over the
course of receiving the byte?). The timings themselves probably are derived
from a formula similar to what appears in the C64 Programmer's Reference
Guide (the only variable in the formula being the clock frequency).

But does the C128 CIA work reliably at 2 MHz anyway? Again if IIRC, all I/O
on the C128 is done at 1 MHz, even if the machine is in 2 MHz mode.

Anton Treuenfels


"Christian Johansson" <c...@combort.se> wrote in message

news:EjP%b.84354$dP1.2...@newsc.telia.net...

Christian Johansson

unread,
Mar 2, 2004, 12:56:30 PM3/2/04
to
"Anton Treuenfels" <teamt...@yahoo.com> skrev i meddelandet
news:ABT0c.29166$hm4....@newsread3.news.atl.earthlink.net...

>
> But does the C128 CIA work reliably at 2 MHz anyway? Again if IIRC, all
I/O
> on the C128 is done at 1 MHz, even if the machine is in 2 MHz mode.
>
> Anton Treuenfels

Yes, I have transferred a lot of data at 2400 bps at 2 MHz using the
standard Kernal routines without any errors. However, at 1 MHz, the maximum
baud rate I can get working reliably with the standard Kernal routines is
1200 bps.

Btw, I have looked more at the Over5 source code and it doesn't seem so
difficult so I will probably try to skip using NMIs altogether and do
something like in Over5. WhiteFlame wrote that he could get 57600 bps
working on the C64 so it might be possible to get 115200 bps working on the
C128 in 2 MHz mode.


Matthew Montchalin

unread,
Mar 2, 2004, 5:06:43 PM3/2/04
to
On Tue, 2 Mar 2004, Christian Johansson wrote:
|Yes, I have transferred a lot of data at 2400 bps at 2 MHz using the
|standard Kernal routines without any errors. However, at 1 MHz, the maximum
|baud rate I can get working reliably with the standard Kernal routines is
|1200 bps.
|
|Btw, I have looked more at the Over5 source code and it doesn't seem so
|difficult so I will probably try to skip using NMIs altogether and do
|something like in Over5. WhiteFlame wrote that he could get 57600 bps
|working on the C64 so it might be possible to get 115200 bps working on the
|C128 in 2 MHz mode.

FWIW, I managed to get 1800 baud going reliably with the standard C-64
Kernal routines. Amazingly, many ordinary 1200 baud modems not good
enough to be called "2400 baud modems" could achieve 1800 baud connections.

0 new messages