Update patch, add basic test prg, rewrite TX test code
This commit is contained in:
parent
42b1c29509
commit
3a59b292a4
4 changed files with 163 additions and 38 deletions
7
Makefile
7
Makefile
|
@ -4,13 +4,16 @@ MEMCFG := mem.cfg
|
|||
|
||||
.PHONY: all clean
|
||||
|
||||
all: rs232.prg
|
||||
all: rs232.prg test.prg
|
||||
|
||||
clean:
|
||||
rm -f rs232.bin *.o *.lst *.map
|
||||
rm -f *.o *.lst *.map *.prg
|
||||
|
||||
rs232.prg: driver.o
|
||||
cl65 -v -C ${MEMCFG} -m rs232.map -o $@ $^
|
||||
|
||||
test.prg: test.bas
|
||||
petcat -w40 -o $@ -- $^
|
||||
|
||||
%.o: %.a65
|
||||
ca65 -v -l $(patsubst %.o,%.lst,$@) -t ${MACHINE} -o $@ $<
|
||||
|
|
143
driver.a65
143
driver.a65
|
@ -20,7 +20,7 @@ PIA2_CRB := PIA2+$3
|
|||
; 1MHz phase2 clock
|
||||
PHI2_CLOCK = 1000000
|
||||
; don't set the baud rate too high, or other operations will be starved
|
||||
BAUD_RATE = 300
|
||||
BAUD_RATE = 600
|
||||
|
||||
; parameters and return values can reside in an unused area of the zero page
|
||||
.zeropage
|
||||
|
@ -40,8 +40,9 @@ BAUD_RATE = 300
|
|||
rs_status: .byte 0
|
||||
; TODO add a state variable that can be monitored by the BASIC WAIT command
|
||||
|
||||
; this is load address, for generating PRG files
|
||||
; works, as long as the code segment comes right after these two bytes
|
||||
; this is for the load address, so we can generate PRG files.
|
||||
; works as long as the code segment comes right after these two bytes,
|
||||
; i.e. as long as the LOADADDR segment resides at $load_address - 2
|
||||
.segment "LOADADDR"
|
||||
.export LOADADDR
|
||||
LOADADDR:
|
||||
|
@ -49,6 +50,10 @@ BAUD_RATE = 300
|
|||
|
||||
; entry points
|
||||
.code
|
||||
; for development and testing
|
||||
.export rs_test
|
||||
rs_test:
|
||||
jmp test
|
||||
; these are convenience entry points right at the beginning of the page,
|
||||
; to reduce dependency on code size.
|
||||
; relocatable code would be perfect, but that's a lot more work.
|
||||
|
@ -94,8 +99,108 @@ BAUD_RATE = 300
|
|||
; main code follows
|
||||
.code
|
||||
|
||||
;FIXME BEGIN
|
||||
; test code:
|
||||
; single-byte RS-232 transmission
|
||||
; this code runs asynchronously in a VIA T2 interrupt handler
|
||||
; the handler is installed and uninstalled automatically until
|
||||
; transmission is complete.
|
||||
; do not call this too quickly in succession, or you will lock
|
||||
; up the CPU in an endless loop.
|
||||
test:
|
||||
sei
|
||||
|
||||
lda #10
|
||||
sta rs_available
|
||||
lda rs_data
|
||||
asl a
|
||||
sta rs_data
|
||||
lda #%00000001
|
||||
rol a
|
||||
sta rs_data+1
|
||||
|
||||
lda VIA_DDRB
|
||||
ora #%00001000
|
||||
sta VIA_DDRB
|
||||
|
||||
lda IRQVec
|
||||
ldx IRQVec+1
|
||||
sta oldvector
|
||||
stx oldvector+1
|
||||
lda #<irqtest
|
||||
ldx #>irqtest
|
||||
sta IRQVec
|
||||
stx IRQVec+1
|
||||
|
||||
lda VIA_CR
|
||||
and #%11011111
|
||||
sta VIA_CR
|
||||
lda #%10100000
|
||||
sta VIA_IER
|
||||
testperiod = PHI2_CLOCK/BAUD_RATE
|
||||
lda #<testperiod
|
||||
ldx #>testperiod
|
||||
sta VIA_T2CL
|
||||
stx VIA_T2CH
|
||||
rts
|
||||
|
||||
cli
|
||||
rts
|
||||
|
||||
irqtest:
|
||||
sei
|
||||
cld
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
|
||||
lda VIA_IFR
|
||||
and #%00100000
|
||||
beq @irqtestend
|
||||
|
||||
ldx rs_available
|
||||
beq @irqtestrestore
|
||||
dex
|
||||
stx rs_available
|
||||
|
||||
;lda #<testperiod
|
||||
ldx #>testperiod
|
||||
;sta VIA_T2CL
|
||||
stx VIA_T2CH
|
||||
|
||||
lsr rs_data+1
|
||||
ror rs_data
|
||||
lda VIA_PB
|
||||
bcc @irqtestblank
|
||||
ora #%00001000
|
||||
bcs @irqtestwrite
|
||||
@irqtestblank:
|
||||
and #%11110111
|
||||
@irqtestwrite:
|
||||
sta VIA_PB
|
||||
|
||||
@irqtestend:
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
jmp (oldvector)
|
||||
|
||||
@irqtestrestore:
|
||||
lda #%00100000
|
||||
sta VIA_IER
|
||||
lda oldvector
|
||||
ldx oldvector+1
|
||||
sta IRQVec
|
||||
stx IRQVec+1
|
||||
clv
|
||||
bvc @irqtestend
|
||||
;FIXME END
|
||||
|
||||
; driver installation, must be called once to set up IRQs, etc.
|
||||
install:
|
||||
; FIXME this doesn't work if we're loading from ROM
|
||||
lda #1
|
||||
sta initialized
|
||||
; check if we're already initialized
|
||||
lda initialized
|
||||
beq @hwinit
|
||||
|
@ -107,7 +212,7 @@ BAUD_RATE = 300
|
|||
|
||||
@hwinit:
|
||||
; disable interrupts, so we're not disturbed
|
||||
cli
|
||||
sei
|
||||
|
||||
; save the previous handler
|
||||
lda IRQVec
|
||||
|
@ -115,9 +220,9 @@ BAUD_RATE = 300
|
|||
lda IRQVec+1
|
||||
sta oldvector+1
|
||||
; assign our custom interrupt handler to the IRQ vector
|
||||
lda #>irqhandler
|
||||
sta IRQVec
|
||||
lda #<irqhandler
|
||||
sta IRQVec
|
||||
lda #>irqhandler
|
||||
sta IRQVec+1
|
||||
|
||||
; VIA timer 2 cannot be enabled and disabled on the fly, so we'll
|
||||
|
@ -163,7 +268,7 @@ BAUD_RATE = 300
|
|||
lda #rs_status_ok
|
||||
sta rs_status
|
||||
; fire away
|
||||
sei
|
||||
cli
|
||||
; return
|
||||
rts
|
||||
|
||||
|
@ -181,7 +286,7 @@ BAUD_RATE = 300
|
|||
|
||||
@hwuninit:
|
||||
; disable interrupts, so we're not disturbed
|
||||
cli
|
||||
sei
|
||||
|
||||
; restore the previous handler
|
||||
lda oldvector
|
||||
|
@ -210,7 +315,7 @@ BAUD_RATE = 300
|
|||
lda #rs_status_ok
|
||||
sta rs_status
|
||||
; the rest of the system will probably want interrupts enabled
|
||||
sei
|
||||
cli
|
||||
; return
|
||||
rts
|
||||
|
||||
|
@ -230,7 +335,7 @@ BAUD_RATE = 300
|
|||
|
||||
@dowrite:
|
||||
; disable interrupts while we write into the buffer
|
||||
cli
|
||||
sei
|
||||
|
||||
; test if there's space in the buffer first
|
||||
lda #16
|
||||
|
@ -275,7 +380,7 @@ BAUD_RATE = 300
|
|||
|
||||
@wrdone:
|
||||
; and re-enable
|
||||
sei
|
||||
cli
|
||||
|
||||
; return
|
||||
rts
|
||||
|
@ -295,7 +400,7 @@ BAUD_RATE = 300
|
|||
|
||||
@doread:
|
||||
; disable interrupts while we read the buffer
|
||||
cli
|
||||
sei
|
||||
|
||||
; test if we have any data first
|
||||
lda inqlen
|
||||
|
@ -339,7 +444,7 @@ BAUD_RATE = 300
|
|||
|
||||
@rddone:
|
||||
; and re-enable
|
||||
sei
|
||||
cli
|
||||
|
||||
; return
|
||||
rts
|
||||
|
@ -348,9 +453,9 @@ BAUD_RATE = 300
|
|||
starttimer:
|
||||
; arm VIA timer 2 by latching the counter
|
||||
period = PHI2_CLOCK/BAUD_RATE
|
||||
lda #>period
|
||||
sta VIA_T1CL
|
||||
lda #<period
|
||||
sta VIA_T1CL
|
||||
lda #>period
|
||||
sta VIA_T1CH
|
||||
; return
|
||||
rts
|
||||
|
@ -368,7 +473,7 @@ BAUD_RATE = 300
|
|||
.interruptor irqhandler
|
||||
irqhandler:
|
||||
; disable interrupts
|
||||
cli
|
||||
sei
|
||||
; clear decimal flag to avoid unexpected behavior
|
||||
cld
|
||||
; save registers
|
||||
|
@ -546,9 +651,9 @@ BAUD_RATE = 300
|
|||
pla
|
||||
tax
|
||||
pla
|
||||
; enable interrupts
|
||||
; FIXME should we do this here? what's common practice? is this compatible with other interrupt handlers
|
||||
sei
|
||||
; we don't need to re-enable interrupts here,
|
||||
; this will happen automatically when flags are restored later
|
||||
;cli
|
||||
; jump to previous handler
|
||||
; we don't need to return or restore flags, this will be done by the chained interrupt handler
|
||||
jmp (oldvector)
|
||||
|
|
15
test.bas
Normal file
15
test.bas
Normal file
|
@ -0,0 +1,15 @@
|
|||
10 print "initializing driver"
|
||||
20 poke 238, asc("h")
|
||||
30 sys 28672
|
||||
120 end
|
||||
200 print "sending data"
|
||||
210 poke 238, asc("a")
|
||||
220 sys 28681
|
||||
230 s = peek(239)
|
||||
240 if s = 0 then goto 40
|
||||
250 print "status "; s
|
||||
260 goto 40
|
||||
300 print
|
||||
310 print "disabling driver"
|
||||
320 sys 28675
|
||||
430 end
|
|
@ -12,10 +12,10 @@ index cd38b6d..cf10121 100644
|
|||
+ tape-rs232.h
|
||||
diff --git a/src/tapeport/tape-rs232.c b/src/tapeport/tape-rs232.c
|
||||
new file mode 100644
|
||||
index 0000000..2b66748
|
||||
index 0000000..680a1c4
|
||||
--- /dev/null
|
||||
+++ b/src/tapeport/tape-rs232.c
|
||||
@@ -0,0 +1,268 @@
|
||||
@@ -0,0 +1,270 @@
|
||||
+/*
|
||||
+ * tape-rs232.h: RS-232 interface for the PET, connected to the tape port
|
||||
+ *
|
||||
|
@ -130,7 +130,7 @@ index 0000000..2b66748
|
|||
+ dev->read_shift = 0;
|
||||
+ dev->write_shift = 0;
|
||||
+ /* TODO make the baud rate customizable */
|
||||
+ dev->baudrate = 300;
|
||||
+ dev->baudrate = 600;
|
||||
+ if (dev->write_alarm != NULL) {
|
||||
+ alarm_destroy(dev->write_alarm);
|
||||
+ }
|
||||
|
@ -186,7 +186,7 @@ index 0000000..2b66748
|
|||
+ alarm_unset(dev->read_alarm);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+CLOCK last_maincpu_clk = 0;
|
||||
+static void tape_rs232_write(int port, int write_bit)
|
||||
+{
|
||||
+ if (port < 0 || port >= TAPEPORT_MAX_PORTS) {
|
||||
|
@ -201,7 +201,9 @@ index 0000000..2b66748
|
|||
+ /* FIXME RS-232 bit-clocking must be done on a time base, not on level change interrupts */
|
||||
+ /* write_bit is actually the contents of VIA_PB, and the write bit is PB3, i.e. 0x08 */
|
||||
+ dev->write_level = (write_bit & 0x08) != 0 ? 1 : 0;
|
||||
+ log_debug("tape_rs232: write port %d level %d", port, dev->write_level);
|
||||
+ long delta = (long) maincpu_clk - last_maincpu_clk;
|
||||
+ last_maincpu_clk = maincpu_clk;
|
||||
+ log_debug("tape_rs232: write port %d level %d clock %lu delta %ld", port, dev->write_level, maincpu_clk, delta);
|
||||
+ /* write in progress? */
|
||||
+ if (dev->write_shift == 0) {
|
||||
+ /* no, start a new transmission - but only if this was a high->low transition (start bit) */
|
||||
|
@ -212,7 +214,7 @@ index 0000000..2b66748
|
|||
+ /* arm the first baud rate timer: sample in the middle of each bit,
|
||||
+ * so the delay must be half a baud clock cycle */
|
||||
+ clock_t delay = machine_get_cycles_per_second() / (dev->baudrate * 2);
|
||||
+ log_debug("tape_rs232: schedule start bit alarm after %d clock cycles", delay);
|
||||
+ //log_debug("tape_rs232: schedule start bit alarm after %ld clock cycles", delay);
|
||||
+ alarm_set(dev->write_alarm, (CLOCK) (maincpu_clk + delay));
|
||||
+ }
|
||||
+ }
|
||||
|
@ -224,24 +226,24 @@ index 0000000..2b66748
|
|||
+ return;
|
||||
+ }
|
||||
+ tape_rs232_state_t *dev = (tape_rs232_state_t *) data;
|
||||
+ /* is this the first bit of a transmission? */
|
||||
+ if (dev->write_shift == 10) {
|
||||
+ /* yes, reconfigure the timer to run at baudrate intervals */
|
||||
+ alarm_unset(dev->write_alarm);
|
||||
+ clock_t delay = machine_get_cycles_per_second() / dev->baudrate;
|
||||
+ log_debug("tape_rs232: schedule regular bit alarm after %d clock cycles", delay);
|
||||
+ alarm_set(dev->write_alarm, (CLOCK) (maincpu_clk + delay));
|
||||
+ }
|
||||
+ /* shift in the next bit */
|
||||
+ dev->write_register = (dev->write_register << 1) | dev->write_level;
|
||||
+ /* prepare the next alarm, at baudrate interval */
|
||||
+ alarm_unset(dev->write_alarm);
|
||||
+ clock_t delay = machine_get_cycles_per_second() / dev->baudrate;
|
||||
+ //log_debug("tape_rs232: schedule regular bit alarm after %ld clock cycles", delay);
|
||||
+ alarm_set(dev->write_alarm, (CLOCK) (maincpu_clk + delay));
|
||||
+ /* shift in the next bit, from the head */
|
||||
+ dev->write_register = (dev->write_register >> 1) | (dev->write_level ? 0x200 : 0x000);
|
||||
+ /* and decrement the counter */
|
||||
+ --dev->write_shift;
|
||||
+ long delta = (long) maincpu_clk - last_maincpu_clk;
|
||||
+ last_maincpu_clk = maincpu_clk;
|
||||
+ log_debug("tape_rs232: sample %d reg 0x%04x offset %lu clock %lu delta %ld shift %u", dev->write_level, dev->write_register, offset, maincpu_clk, delta, dev->write_shift);
|
||||
+ /* transmission complete? */
|
||||
+ if (dev->write_shift == 0) {
|
||||
+ /* yes, unarm the timer */
|
||||
+ alarm_unset(dev->write_alarm);
|
||||
+ /* check if start and stop bits are valid */
|
||||
+ if ((dev->write_register & 0x0200) == 0x0000 && (dev->write_register & 0x0001) == 0x0001) {
|
||||
+ if ((dev->write_register & 0x0200) == 0x0200 && (dev->write_register & 0x0001) == 0x0000) {
|
||||
+ /* yes, output data byte */
|
||||
+ uint8_t wbyte = (uint8_t) (dev->write_register >> 1);
|
||||
+ /* utf-8 codes generated by the petcii conversion routine can be max. 4 bytes (plus terminator) */
|
||||
|
|
Loading…
Reference in a new issue