diff --git a/libsrc/atari7800/setcursor.s b/libsrc/atari7800/setcursor.s
new file mode 100644
index 000000000..9f8765656
--- /dev/null
+++ b/libsrc/atari7800/setcursor.s
@@ -0,0 +1,169 @@
+;
+; 2022-04-03, Karri Kaksonen
+;
+; setcursor (unsigned char on);
+;
+; The Atari 7800 does not have a hw cursor.
+; This module emulates a cursor to be used with the conio
+; implementation.
+;
+; The actual cursor display is included in the conio dll
+; but every scanline has the element silenced by default
+; at the end of every zone.
+;
+; The way the cursor works is to silence it before the cursor
+; position changes and enable it afterwards.
+;
+; In order to get some performance we have a pointer to the
+; cursor header structure. This structure is always at the
+; end of the zone. So the pointer changes when CURS_Y changes.
+;
+; There is so many dependencies that it makes sense to
+; deal with all CURS_X, CURS_Y stuff in this file and
+; definitely not allow direct access to the variables.
+;
+
+        .export         _cursor_visible
+        .export         _cursorzone
+        .export         _gotoxy, _gotox, _gotoy, wherex, wherey
+        .constructor    init_cursor
+
+	.importzp	ptr1, sp
+	.import		_zones
+	.import		pusha, incsp1, pusha0, pushax, popax, tosumula0, incax5
+        .include        "atari7800.inc"
+
+        .macpack        generic
+
+	.data
+CURS_X:
+	.byte	0
+CURS_Y:
+	.byte	0
+_cursor_visible:
+        .byte	1
+
+	.bss
+_cursorzone:
+	.res	2
+
+	.code
+
+;-----------------------------------------------------------------------------
+; Calculate cursorzone address
+; You also need to set the cursorzone to point to the correct cursor Header
+; at the end of line CURS_Y.
+; Offset to cursor zone 5. To next line offset 11
+; cursorzone points to _zones + CURS_Y * 11 + 5
+; A = CURS_Y
+calccursorzone:
+	jsr	pusha0
+	lda	#11
+	jsr	tosumula0
+	jsr	incax5
+	clc
+	adc	#<_zones
+	sta	_cursorzone	; calculate new cursorzone
+	sta	ptr1
+	txa
+	adc	#>_zones
+	sta	_cursorzone+1
+	sta	ptr1+1
+	rts
+
+;-----------------------------------------------------------------------------
+; Set cursor to Y position.
+; You also need to set the cursorzone to point to the correct cursor Header
+; at the end of line CURS_Y.
+; Offset to cursor zone 5. To next line offset 11
+; cursorzone points to _zones + CURS_Y * 11 + 5
+;
+; cursorzone[1] = 0 when not CURS_Y, 30 if CURS_Y
+;
+; Disable cursor
+; cursorzone[1] = 0
+;
+; Enable cursor
+; if showcursor cursorzone[1] = 30
+;
+_gotoy:	pha
+	lda	CURS_Y
+	jsr	calccursorzone
+	ldy	#1
+	lda	#0
+	sta	(ptr1),y	; disable cursor
+	pla
+	sta	CURS_Y
+	jsr	calccursorzone
+	lda	_cursor_visible
+	beq	L2
+	lda	#30		; enable cursor
+L2:	ldy	#1
+	sta	(ptr1),y
+	rts
+
+;-----------------------------------------------------------------------------
+; Set cursor to X position.
+; You also need to set the hpos offset to the correct value on this line
+; cursorzone[3] = 8 * CURS_X
+;
+_gotox:	tay
+	lda	_cursorzone
+	ldx	_cursorzone+1
+	sta	ptr1
+	stx	ptr1+1
+	tya
+	ldy	#3
+	clc
+	rol
+	rol
+	rol
+	sta	(ptr1),y
+	rts
+
+;-----------------------------------------------------------------------------
+; Set cursor to desired position (X,Y)
+;
+	.proc   _gotoxy
+
+	jsr	_gotoy
+	jsr	popax
+	jsr	_gotox
+	rts
+	.endproc
+
+;-----------------------------------------------------------------------------
+; Get cursor X position
+;
+	.proc   wherex
+
+	lda	CURS_X
+	jsr	pusha0
+	rts
+	.endproc
+
+;-----------------------------------------------------------------------------
+; Get cursor Y position
+;
+	.proc   wherey
+
+	lda	CURS_Y
+	jsr	pusha0
+	rts
+	.endproc
+
+;-----------------------------------------------------------------------------
+; Initialize cursorzone at startup
+; Offset to cursor zone 5.
+;
+;        .segment        "ONCE"
+init_cursor:
+	lda	#0
+	jsr	calccursorzone
+	rts
+
+;-----------------------------------------------------------------------------
+; force the init constructor to be imported
+
+                .import initconio
+conio_init      = initconio