X16 User Port Usage Example (that KX-P2023 printer!!)

For posting library code and examples.
Post Reply
Xiphod
Posts: 599
Joined: Thu Apr 15, 2021 8:05 am

X16 User Port Usage Example (that KX-P2023 printer!!)

Post by Xiphod »

For reference, here is a reminder on the kind of "X16 kickoff" video
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

Last edited by Xiphod on Wed Feb 05, 2025 1:56 pm, edited 17 times in total.
Xiphod
Posts: 599
Joined: Thu Apr 15, 2021 8:05 am

Re: X16 User Port Usage Example (that KX-P2023 printer!!)

Post by Xiphod »

Also, if the long BYTE sequences don't make sense, attached is an Excel version that will help it make sense.
Attachments
logo.jpg
logo.jpg (202.42 KiB) Viewed 10503 times
logo.xlsx
(31.38 KiB) Downloaded 141 times
Xiphod
Posts: 599
Joined: Thu Apr 15, 2021 8:05 am

Re: X16 User Port Usage Example (that KX-P2023 printer!!)

Post by Xiphod »

Notes on the 65C22 chip connected to the UserPort pins is here:
https://github.com/X16Community/x16-doc ... ramming.md
https://eater.net/datasheets/w65c22.pdf

Mainly:
VIA#1 at address $9F00 - $9F0F
VIA#2 at address $9F10 - $9F1F
(and that VIA #2 can be toggled to IRQ or NMI)

The actual UserPort pinout is
https://github.com/X16Community/x16-doc ... ardware.md
or as follows:
Attachments
userport.jpg
userport.jpg (140.33 KiB) Viewed 10501 times
Xiphod
Posts: 599
Joined: Thu Apr 15, 2021 8:05 am

Re: X16 User Port Usage Example (that KX-P2023 printer!!)

Post by Xiphod »

Note that technically you could "bit bang" RS-232 out of any pair of these data pins, but said pins would then also need to be connected to a 'level shifter" to be interpreted as RS-232 by the connected system. And this would be full-CPU and yield at best 9600 baud.
(the CD carrier detect is mostly optional)

Something like this (to go from 5V TTL to a -12V to +12V swing):
Attachments
levelshifters.jpg
levelshifters.jpg (81.95 KiB) Viewed 10477 times
Xiphod
Posts: 599
Joined: Thu Apr 15, 2021 8:05 am

Re: X16 User Port Usage Example (that KX-P2023 printer!!)

Post by Xiphod »

Reporting that I have the X16 talking to a PC via the UserPort. I'm not aware of any ready-made 26-pin to DB25 25-pin adapters, so got one of those green "breakout box" adapter things to wire it up myself.

I was messing with LAPLink and FileMaven recently for something else, so got to thinking a similar thing ought to be possible here.
.

Confirm on a logic scope that all the expected bytes were getting received. I also messed with the ParallelPort Tester under Windows XP, saw some blinky lights there going.


DSViewconfirm2.jpg
DSViewconfirm2.jpg (1.67 MiB) Viewed 1121 times
DSViewConfirm.jpg
DSViewConfirm.jpg (588.43 KiB) Viewed 1121 times
X16toPCparallel.jpg
X16toPCparallel.jpg (186.72 KiB) Viewed 1121 times
Attachments
sample2.a.txt
(6.94 KiB) Downloaded 45 times
Last edited by Xiphod on Sat Apr 05, 2025 6:39 am, edited 2 times in total.
Xiphod
Posts: 599
Joined: Thu Apr 15, 2021 8:05 am

Re: X16 User Port Usage Example (that KX-P2023 printer!!)

Post by Xiphod »

Compiled the sample.a using ACME 6502 assembler

Only change was having these two lines at the top

Code: Select all

!to "SAMPLE2.PRG", cbm
!cpu 6502
And uncommenting some of the data-direction setup code.

Feeling like a LAPLINK cable to xfer data between these systems is going to be possible.
Attachments
wiringup.jpg
wiringup.jpg (474.72 KiB) Viewed 1117 times
Xiphod
Posts: 599
Joined: Thu Apr 15, 2021 8:05 am

Re: X16 User Port Usage Example (that KX-P2023 printer!!)

Post by Xiphod »

C version of the prior ACME code. PRG compiled with cc65. Spams "commander x16" out of the UserPort in a loop. DSView filtered to show the bytes as ASCII.
IMG_4277A.jpg
IMG_4277A.jpg (231.74 KiB) Viewed 565 times
NOTE: Updated userport.c (added comments and re-arranged order a little bit, but resulting PRG build is the same)
NOTE: No optimization was enabled when doing the cc65 build. Not sure if this impacts things, but noting it in case others setup scripts to do the build.
Attachments
userport.c
(2.42 KiB) Downloaded 28 times
USERPORT.PRG
(364 Bytes) Downloaded 22 times
Xiphod
Posts: 599
Joined: Thu Apr 15, 2021 8:05 am

Re: X16 User Port Usage Example (that KX-P2023 printer!!)

Post by Xiphod »

Keyboard controlled output to the Data Pins on the UserPort. Kind of an "interactive" version of the typical beginner Arduino project.

KEY 1-7 to toggle each of DataPins 1-7
KEY 8 Toggles the current state of all the data pins
KEY 9 Turns all Data Pins ON
KEY 0 Turns all Data Pins OFF

A / [ Rotate Left
D / ] Rotate Right

Attachments
userport2.c
(5.09 KiB) Downloaded 9 times
Post Reply