https://www.youtube.com/watch?v=gtC8pra1ozM
Here is a trimmed down and more-annotated version of Kevin's original "printer example" 6502 code. Whether you have a printer or not, this is an example of how to interact with the VIA #2 UserPort pins.
VIA #1 is used to support the gamepads, system clock, and IEC port. VIA #2 is open for user defined usage.
Hint: such as synchronous parallel send of a full byte across a parallel cable.
Remember, you'll need to have ordered and installed the VIA #2 chip and parts to make use of this UserPort stuff. X16 UserPort is not identical to the C64 UserPort (and none of the C64 KERNAL support for UserPort is present in the X16 KERNAL)
There are a couple aspects of the code here that I don't yet fully understand, but I think they might just be "Epson printer-isms." Hopefully some other experts can chime in.
- Main thing I didn't understand is why OUTB is used to toggle a strobe pin. I assume it's a printer-thing, to sort of say "I'm done sending you commands"? I noticed in the Epson manual that printer has a sizeable buffer. Just why on OUTB instead of OUTA or vice versa? (or maybe it doesn't matter, just use OUTB to avoid messing up the setup in progress on OUTA?)
EDIT: I suppose it is needed after each character to indicate when you've settled on the state of the parallel data bits. And it may be a printer-ism on deciding which set of pins is used as the strobe.
- Secondary was the duration of the DELAY. I get that some amount of delay is needed between each printed character. The printer manual PDF didn't have any particular spec on a baud-rate. Being parallel, maybe it doesn't quite work like that. Not sure if my accounting is correct, but I came up with a ~7ms delay? My other guess is that a slow delay is just for effect during the video, and to keep the noise down? Or it was just settling for "that works good enough" after trial and error.
(in any case, as noted in the comments, using one of the timers built in the VIA #2 would have been more elegant)
EDIT: ah I estimated at 1 MHz, and we're running at 8MHz... so closer to a 1ms delay?
Code: Select all
; printer test program for Commander X16
*=$0801 ;START ADDRESS IS $0801
; to clarify the BYTE sequence below...
; addr
; $0801 $0C "NEXT" COMMAND ADDRESS
; $0802 $08
; $0803 $0A line "10" (LO)
; $0804 $00 (HI)
; $0805 $9E SYS
; $0806 $32 2
; $0807 $30 0
; $0808 $36 6
; $0809 $34 4
; $080A $00 pad (clearly marks end of the tokenized BASIC)
; $080B $00 pad
; $080C $00 pad
BASIC: !BYTE $0C,$08,$0A,$00,$9E,$32,$30,$36,$34,$00,$00,$00
;Adds BASIC line: 10 SYS 2064
*=$810 ; i.e. SYS 2064 or SYS $0810
jmp startit
LINE2:
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
LINE4:
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4E, $4E, $57, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $57, $4E, $4E, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4B, $6C, $6F, $4B, $57, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
!BYTE $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $57, $30, $6F, $6C, $4B, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D, $4D
startit:
; not sure why all this is commented out... seems like port direction is important?
; (maybe just assumes a power-on default)
; or maybe in testing, an alternate prior runtime had already set all this
;LDA #$FF
;STA $9F13 ;Direction for Port A All Output
;JSR DELAY
;LDA #$D1
;STA $9F12 ;Direction for Port B 11010001
;JSR DELAY
;LDA #$00
;STA $9F1A ;Disable shift register
;JSR DELAY
;STA $9F1B ;Disable Aux Control Register
;JSR DELAY
;LDA #$23
;STA $9F1C ;PCR PERIPHERAL CONTROL REGISTER
;JSR DELAY
;LDA #$00
;STA $9F1E ;IER INTERRUPT ENABLE REGISTER
;JSR DELAY
; JSR CHKBUSY
; JSR printlogo
; (there was some other commented out test code here that I don't think was important)
printlogo:
LOOPC=$70 ; ZERO PAGE, $0022 - $007F AVAILABLE TO USER [ $70 == offset 112 ]
; EVEN ROW = RESET BACK TO 0 OFFSET
LDA #$00 ; REG.A = $00
STA LOOPC ; MEM[ $70 ] = REG.A
LO1: LDY LOOPC ; REG.Y == MEM[ $70 ] == 0..1..2..3.. roll over at 255
LDA LINE2,Y ; REG.A == MEM[ LINE2 + Y ]
STA $9F11 ; MEM[$9F11] = REG.A -----> write to ORA (output register A)
JSR STROBE ; each output must be strobed, not sure why (printer-ism?)
INC LOOPC ; increment to next storage-byte
LDA LOOPC ; check for end of line
CMP #$7F ; 128 chars per row (physical on paper, but the BYTE sequence keeps going)
BNE LO1 ; first half of data sequence
LDX #$0D ; CR return
STX $9F11
JSR STROBE
LDX #$0A ; LF next line
STX $9F11
JSR STROBE
; data wise, we have a long 128 byte sequence. don't reset the index pointer,
; proceed with that data buffer to now work the "second half" of that data sequence
INC LOOPC
LO2: LDY LOOPC
LDA LINE2,Y
STA $9F11
JSR STROBE
INC LOOPC
LDA LOOPC ; check for end of line
CMP #$FF ; this portion continues from $7F (single byte counter, max 255)
BNE LO2
LDX #$0D ; CR (return)
STX $9F11
JSR STROBE
LDX #$0A ; LF next line
STX $9F11
JSR STROBE
INC LOOPC
; EVEN ROW = RESET BACK TO 0 OFFSET (repeat the same overall pattern as L01)
LDA #$00
STA LOOPC
LO3: LDY LOOPC
LDA LINE4,Y
STA $9F11
JSR STROBE
INC LOOPC
LDA LOOPC ; check for end of line
CMP #$7F
BNE LO3
LDX #$0D ;CR+LF next line
STX $9F11
JSR STROBE
LDX #$0A
STX $9F11
JSR STROBE
INC LOOPC
LO4: LDY LOOPC
LDA LINE4,Y
STA $9F11
JSR STROBE
INC LOOPC
LDA LOOPC ; check for end of line
CMP #$FF
BNE LO4
LDX #$0D ;CR+LF next line
STX $9F11
JSR STROBE
LDX #$0A
STX $9F11
JSR STROBE
INC LOOPC
; repeat the same pattern as above for however much next rows you want to print... (and have BYTE sequences defined for)
RTS ; will this drop back to BASIC? (should)
STROBE:
;no_interrupt:
; JSR DELAY
; LDA $9F14 ; IFR register address
; AND #$02 ; Mask for CA1
; BEQ no_interrupt ; Branch if no interrupt
LDY #%11010000 ;pulse strobe pin WHY?
STY $9F10 ; ORB = output register B
JSR DELAY
LDY #%11010001 ; twiddle bit 0 back ON
STY $9F10
JSR DELAY
RTS
;CHKBUSY:
; LDA #$80
; AND $9F10 ;check bit 7 for busy flag
; CMP #$80
; BEQ CHKBUSY
; RTS
DELAY: ;should use a timer instead probably.
LDY #00
D1: NOP:NOP:NOP:NOP:NOP:NOP:NOP:NOP:NOP:NOP:NOP:NOP:NOP:NOP:NOP
INY
CPY #00 ; 255 * 15 = 3825 (NOP is 2 cycles, 7650 cycles) ~7.65ms delay @ 1MHz ?
BNE D1
RTS