From 057884ffa921eb2b19b152a1623da4d318d17e12 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Thu, 2 Jan 2014 19:28:22 +0100
Subject: [PATCH 01/31] Atari: initial support to create cassette bootable
 programs -- unfinished

---
 cfg/atari-cassette.cfg | 53 ++++++++++++++++++++++++++++++++++++++++++
 libsrc/atari/cashdr.s  | 40 +++++++++++++++++++++++++++++++
 2 files changed, 93 insertions(+)
 create mode 100644 cfg/atari-cassette.cfg
 create mode 100644 libsrc/atari/cashdr.s

diff --git a/cfg/atari-cassette.cfg b/cfg/atari-cassette.cfg
new file mode 100644
index 000000000..ba640a54e
--- /dev/null
+++ b/cfg/atari-cassette.cfg
@@ -0,0 +1,53 @@
+FEATURES {
+    STARTADDRESS: default = $1000;
+}
+SYMBOLS {
+_cas_init: type = import;
+    __STACKSIZE__:       type = weak, value = $0800; # 2k stack
+    __STARTADDRESS__:    type = export, value = %S;
+    __RESERVED_MEMORY__: type = weak, value = $0000;
+}
+MEMORY {
+    ZP:            file = "", define = yes, start = $0082, size = $007E;
+
+# file header, just $FFFF
+#    HEADER:        file = "",               start = $0000, size = $0002;
+
+#     CASHDR: file = %O, start = $3FD, size = 128, fill = yes;
+     CASHDR: file = %O, start = $0, size = 6;
+# "main program" load chunk
+#    MAINHDR:       file = "",               start = $0000, size = $0004;
+    RAM:           file = %O, define = yes, start = %S,    size = $BC20 - __STACKSIZE__ - __RESERVED_MEMORY__ - %S;
+#    TRAILER:       file = "",               start = $0000, size = $0006;
+}
+SEGMENTS {
+#    EXEHDR:    load = HEADER,     type = ro;
+#    MAINHDR:   load = MAINHDR,    type = ro;
+     CASHDR:   load = CASHDR, type = ro;
+     CASINIT:  load = RAM, type = ro;
+    STARTUP:   load = RAM,        type = ro,  define = yes;
+    LOWCODE:   load = RAM,        type = ro,  define = yes, optional = yes;
+    INIT:      load = RAM,        type = ro,                optional = yes;
+    CODE:      load = RAM,        type = ro,  define = yes;
+    RODATA:    load = RAM,        type = ro;
+    DATA:      load = RAM,        type = rw;
+    BSS:       load = RAM,        type = bss, define = yes;
+    ZEROPAGE:  load = ZP,         type = zp;
+    EXTZP:     load = ZP,         type = zp,                optional = yes;
+#    AUTOSTRT:  load = TRAILER,    type = ro;
+}
+FEATURES {
+    CONDES: type    = constructor,
+            label   = __CONSTRUCTOR_TABLE__,
+            count   = __CONSTRUCTOR_COUNT__,
+            segment = INIT;
+    CONDES: type    = destructor,
+            label   = __DESTRUCTOR_TABLE__,
+            count   = __DESTRUCTOR_COUNT__,
+            segment = RODATA;
+    CONDES: type    = interruptor,
+            label   = __INTERRUPTOR_TABLE__,
+            count   = __INTERRUPTOR_COUNT__,
+            segment = RODATA,
+            import  = __CALLIRQ__;
+}
diff --git a/libsrc/atari/cashdr.s b/libsrc/atari/cashdr.s
new file mode 100644
index 000000000..8d3d0868a
--- /dev/null
+++ b/libsrc/atari/cashdr.s
@@ -0,0 +1,40 @@
+;
+; Cassette boot file header
+;
+; Christian Groessler, chris@groessler.org, 2013
+;
+
+.ifndef __ATARIXL__
+
+	.include "atari.inc"
+
+	.import	__BSS_RUN__, __STARTADDRESS__, start
+	.export	_cas_init
+
+.segment        "CASHDR"
+
+	.byte	0	; ignored
+	.byte	<((__BSS_RUN__ - __STARTADDRESS__ + 127 + 6) / 128)
+	.word	__STARTADDRESS__
+	.word	_cas_init
+
+.segment	"CASINIT"
+
+	lda	#33
+	ldy	#80
+	sta	(SAVMSC),y
+	clc
+	rts
+
+_cas_init:
+	lda	#34
+	ldy	#81
+	sta	(SAVMSC),y
+
+	lda	#<start
+	sta	DOSVEC
+	lda	#>start
+	sta	DOSVEC+1
+	rts
+
+.endif	; .ifdef __ATARIXL__

From 2ab89621a9cee8cf32176f750d9ac9ffedaef7e0 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Thu, 2 Jan 2014 21:45:12 +0100
Subject: [PATCH 02/31] Use deferred VBI instead of immediate VBI. A deferred
 VBI won't be called if the user program has interrupts disabled which makes
 it easier to avoid race conditions.

---
 libsrc/atari/irq.s | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/libsrc/atari/irq.s b/libsrc/atari/irq.s
index 8ec1b12df..9f12d47ed 100644
--- a/libsrc/atari/irq.s
+++ b/libsrc/atari/irq.s
@@ -16,11 +16,11 @@
 .segment        "INIT"
 
 initirq:
-        lda     VVBLKI
-        ldx     VVBLKI+1
+        lda     VVBLKD
+        ldx     VVBLKD+1
         sta     IRQInd+1
         stx     IRQInd+2
-        lda     #6
+        lda     #7
         ldy     #<IRQStub
         ldx     #>IRQStub
         jsr     SETVBV
@@ -31,7 +31,7 @@ initirq:
 .code
 
 doneirq:
-        lda     #6
+        lda     #7
         ldy     IRQInd+1
         ldx     IRQInd+2
         jsr     SETVBV
@@ -44,7 +44,6 @@ doneirq:
 IRQStub:
         cld                             ; Just to be sure
 .ifdef __ATARIXL__
-        pha
 .ifdef CHARGEN_RELOC
         lda     CHBAS
         pha
@@ -64,7 +63,6 @@ IRQStub:
         sta     CHBAS
         sta     CHBASE
 .endif
-        pla
 .endif
         jmp     IRQInd                  ; Jump to the saved IRQ vector
 

From 95c6063f909e6e5c6bf9330e23cb472c12914005 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Fri, 3 Jan 2014 23:40:22 +0100
Subject: [PATCH 03/31] Add "joystick mouse" driver and default mouse callback
 routine (not using P/M graphics).

---
 libsrc/atari/libref.s            |   3 +-
 libsrc/atari/mcbdefault.s        | 114 ++++++++
 libsrc/atari/mou/atrjoy.s        | 443 +++++++++++++++++++++++++++++++
 libsrc/atari/mouse_stat_stddrv.s |  22 ++
 libsrc/atari/mouse_stddrv.s      |  18 ++
 libsrc/atari/mul40.s             |   2 +-
 6 files changed, 600 insertions(+), 2 deletions(-)
 create mode 100644 libsrc/atari/mcbdefault.s
 create mode 100644 libsrc/atari/mou/atrjoy.s
 create mode 100644 libsrc/atari/mouse_stat_stddrv.s
 create mode 100644 libsrc/atari/mouse_stddrv.s

diff --git a/libsrc/atari/libref.s b/libsrc/atari/libref.s
index 8d96ff62d..e6b9df6a5 100644
--- a/libsrc/atari/libref.s
+++ b/libsrc/atari/libref.s
@@ -2,12 +2,13 @@
 ; Oliver Schmidt, 2013-05-31
 ;
 
-        .export         em_libref, joy_libref, tgi_libref, ser_libref
+        .export         em_libref, joy_libref, tgi_libref, ser_libref, mouse_libref
         .import         _exit
         .import         atari_ser_libref
 
 em_libref       := _exit
 joy_libref      := _exit
+mouse_libref    := _exit
 ser_libref      := atari_ser_libref
 .ifdef __ATARIXL__
         .import CIO_handler
diff --git a/libsrc/atari/mcbdefault.s b/libsrc/atari/mcbdefault.s
new file mode 100644
index 000000000..91e920ac4
--- /dev/null
+++ b/libsrc/atari/mcbdefault.s
@@ -0,0 +1,114 @@
+;
+; Default mouse callbacks for the Ataris
+;
+; Christian Groessler, 03.01.2014
+;
+; derived from Apple2 version by
+; Oliver Schmidt, 22.09.2005
+;
+; All functions in this module should be interrupt safe, because they may
+; be called from an interrupt handler
+;
+
+        .export         _mouse_def_callbacks
+        .importzp       tmp4
+        .import         mul40,loc_tmp
+
+        .include        "atari.inc"
+
+; ------------------------------------------------------------------------
+
+        .bss
+
+backup: .res    1
+helper: .res    2
+
+; ------------------------------------------------------------------------
+
+        .segment        "EXTZP" : zeropage
+scrptr: .res    2
+
+; ------------------------------------------------------------------------
+
+
+        .rodata
+
+        ; Callback structure
+_mouse_def_callbacks:
+        .addr   hide
+        .addr   show
+        .addr   movex
+        .addr   movey
+
+; ------------------------------------------------------------------------
+
+        .data
+
+cursor = 11             ; '+' screen code'
+
+; setcursor
+
+getcursor:
+column: ldy     #$00            ; Patched at runtime
+        lda     (scrptr),y         ; Patched at runtime
+        cmp     #cursor
+        rts
+
+setcursor:
+        lda     #cursor
+setscr: sta     (scrptr),y         ; Patched at runtime
+        rts
+
+; ------------------------------------------------------------------------
+
+        .code
+
+done:
+        rts
+
+; Hide the mouse cursor.
+hide:
+        jsr     getcursor       ; Cursor visible at current position?
+        bne     done            ; No, we're done
+        lda     backup          ; Get character at cursor position
+        jmp     setscr          ; Draw character
+
+; Show the mouse cursor.
+show:
+        jsr     getcursor       ; Cursor visible at current position?
+        beq     done            ; Yes, we're done
+        sta     backup          ; Save character at cursor position
+        jmp     setcursor       ; Draw cursor
+
+
+; Move the mouse cursor x position to the value in A/X.
+movex:
+        cpx #0
+        cpx     #1
+        ror     a
+        lsr     a               ; convert to character position
+        lsr     a
+        sta     column+1
+        rts
+
+; Move the mouse cursor y position to the value in A/X.
+movey:
+        tax
+        ldy     tmp4            ; mul40 uses tmp4
+        lda     loc_tmp         ; and this local variable
+        pha
+        txa                     ; get parameter back
+        lsr     a               ; convert y position to character line
+        lsr     a
+        lsr     a
+        jsr     mul40
+        clc
+        adc     SAVMSC
+        sta     scrptr
+        txa
+        adc     SAVMSC+1
+        sta     scrptr+1
+        pla
+        sta     loc_tmp
+        sty     tmp4
+        rts
diff --git a/libsrc/atari/mou/atrjoy.s b/libsrc/atari/mou/atrjoy.s
new file mode 100644
index 000000000..1a5afa71a
--- /dev/null
+++ b/libsrc/atari/mou/atrjoy.s
@@ -0,0 +1,443 @@
+;
+; Driver for a "joystick mouse".
+;
+; C128 version: Ullrich von Bassewitz, 2004-04-05, 2009-09-26
+; Adapted to Atari: Christian Groessler, 2014-01-02
+;
+
+        .include        "zeropage.inc"
+        .include        "mouse-kernel.inc"
+        .include        "atari.inc"
+
+        .macpack        generic
+
+; ------------------------------------------------------------------------
+; Header. Includes jump table
+
+.segment        "JUMPTABLE"
+
+HEADER:
+
+; Driver signature
+
+        .byte   $6d, $6f, $75           ; "mou"
+        .byte   MOUSE_API_VERSION       ; Mouse driver API version number
+
+; Library reference
+
+        .addr   $0000
+
+; Jump table
+
+        .addr   INSTALL
+        .addr   UNINSTALL
+        .addr   HIDE
+        .addr   SHOW
+        .addr   SETBOX
+        .addr   GETBOX
+        .addr   MOVE
+        .addr   BUTTONS
+        .addr   POS
+        .addr   INFO
+        .addr   IOCTL
+        .addr   IRQ
+
+; Mouse driver flags
+
+        .byte   MOUSE_FLAG_LATE_IRQ
+
+; Callback table, set by the kernel before INSTALL is called
+
+CHIDE:  jmp     $0000                   ; Hide the cursor
+CSHOW:  jmp     $0000                   ; Show the cursor
+CMOVEX: jmp     $0000                   ; Move the cursor to X coord
+CMOVEY: jmp     $0000                   ; Move the cursor to Y coord
+
+
+;----------------------------------------------------------------------------
+; Constants
+
+SCREEN_HEIGHT   = 191
+SCREEN_WIDTH    = 319
+
+.enum   JOY
+        UP      = $01
+        DOWN    = $02
+        LEFT    = $04
+        RIGHT   = $08
+.endenum
+
+;----------------------------------------------------------------------------
+; Global variables. The bounding box values are sorted so that they can be
+; written with the least effort in the SETBOX and GETBOX routines, so don't
+; reorder them.
+
+.bss
+
+Vars:
+YPos:           .res    2               ; Current mouse position, Y
+XPos:           .res    2               ; Current mouse position, X
+XMin:           .res    2               ; X1 value of bounding box
+YMin:           .res    2               ; Y1 value of bounding box
+XMax:           .res    2               ; X2 value of bounding box
+YMax:           .res    2               ; Y2 value of bounding box
+Buttons:        .res    1               ; Button mask
+
+
+Temp:           .res    1               ; Temporary value used in the int handler
+visible:        .res    1
+
+; Default values for above variables
+
+.rodata
+
+.proc   DefVars
+        .word   SCREEN_HEIGHT/2         ; YPos
+        .word   SCREEN_WIDTH/2          ; XPos
+        .word   0                       ; XMin
+        .word   0                       ; YMin
+        .word   SCREEN_WIDTH            ; XMax
+        .word   SCREEN_HEIGHT           ; YMax
+        .byte   0                       ; Buttons
+.endproc
+
+.code
+
+;----------------------------------------------------------------------------
+; INSTALL routine. Is called after the driver is loaded into memory. If
+; possible, check if the hardware is present.
+; Must return an MOUSE_ERR_xx code in a/x.
+
+INSTALL:
+
+; Initialize variables. Just copy the default stuff over
+
+        ldx     #.sizeof(DefVars)-1
+@L1:    lda     DefVars,x
+        sta     Vars,x
+        dex
+        bpl     @L1
+
+; Be sure the mouse cursor is invisible and at the default location. We
+; need to do that here, because our mouse interrupt handler doesn't set the
+; mouse position if it hasn't changed.
+
+        sei
+        jsr     CHIDE
+        lda     XPos
+        ldx     XPos+1
+        jsr     CMOVEX
+        lda     YPos
+        ldx     YPos+1
+        jsr     CMOVEY
+        cli
+
+; Done, return zero (= MOUSE_ERR_OK)
+
+        ldx     #$00
+        txa
+        rts
+
+;----------------------------------------------------------------------------
+; UNINSTALL routine. Is called before the driver is removed from memory.
+; No return code required (the driver is removed from memory on return).
+
+UNINSTALL       = HIDE                  ; Hide cursor on exit
+
+;----------------------------------------------------------------------------
+; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
+; a counter for calls to show/hide, and the driver entry point is only called
+; if the mouse is currently visible and should get hidden. For most drivers,
+; no special action is required besides hiding the mouse cursor.
+; No return code required.
+
+HIDE:   dec     visible
+        sei
+        jsr     CHIDE
+        cli
+        rts
+
+;----------------------------------------------------------------------------
+; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
+; a counter for calls to show/hide, and the driver entry point is only called
+; if the mouse is currently hidden and should become visible. For most drivers,
+; no special action is required besides enabling the mouse cursor.
+; No return code required.
+
+SHOW:   inc     visible
+        sei
+        jsr     CSHOW
+        cli
+        rts
+
+;----------------------------------------------------------------------------
+; SETBOX: Set the mouse bounding box. The parameters are passed as they come
+; from the C program, that is, a pointer to a mouse_box struct in a/x.
+; No checks are done if the mouse is currently inside the box, this is the job
+; of the caller. It is not necessary to validate the parameters, trust the
+; caller and save some code here. No return code required.
+
+SETBOX: sta     ptr1
+        stx     ptr1+1                  ; Save data pointer
+
+        ldy     #.sizeof (MOUSE_BOX)-1
+        sei
+
+@L1:    lda     (ptr1),y
+        sta     XMin,y
+        dey
+        bpl     @L1
+
+        cli
+        rts
+
+;----------------------------------------------------------------------------
+; GETBOX: Return the mouse bounding box. The parameters are passed as they
+; come from the C program, that is, a pointer to a mouse_box struct in a/x.
+
+GETBOX: sta     ptr1
+        stx     ptr1+1                  ; Save data pointer
+
+        ldy     #.sizeof (MOUSE_BOX)-1
+        sei
+
+@L1:    lda     XMin,y
+        sta     (ptr1),y
+        dey
+        bpl     @L1
+
+        cli
+        rts
+
+;----------------------------------------------------------------------------
+; MOVE: Move the mouse to a new position. The position is passed as it comes
+; from the C program, that is: X on the stack and Y in a/x. The C wrapper will
+; remove the parameter from the stack on return.
+; No checks are done if the new position is valid (within the bounding box or
+; the screen). No return code required.
+;
+
+MOVE:   sei                             ; No interrupts
+
+        pha
+        txa
+        pha
+        jsr     CHIDE
+        pla
+        tax
+        pla
+
+        sta     YPos
+        stx     YPos+1                  ; New Y position
+        jsr     CMOVEY                  ; Set it
+
+        ldy     #$01
+        lda     (sp),y
+        sta     XPos+1
+        tax
+        dey
+        lda     (sp),y
+        sta     XPos                    ; New X position
+
+        jsr     CMOVEX                  ; Move the cursor
+
+        lda     visible
+        beq     @Ret
+        
+        jsr     CSHOW
+
+@Ret:   cli                             ; Allow interrupts
+        rts
+
+;----------------------------------------------------------------------------
+; BUTTONS: Return the button mask in a/x.
+
+BUTTONS:
+        lda     Buttons
+        ldx     #$00
+        rts
+
+;----------------------------------------------------------------------------
+; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
+; No return code required.
+
+POS:    ldy     #MOUSE_POS::XCOORD      ; Structure offset
+
+        sei                             ; Disable interrupts
+        lda     XPos                    ; Transfer the position
+        sta     (ptr1),y
+        lda     XPos+1
+        iny
+        sta     (ptr1),y
+        lda     YPos
+        iny
+        sta     (ptr1),y
+        lda     YPos+1
+        cli                             ; Enable interrupts
+
+        iny
+        sta     (ptr1),y                ; Store last byte
+
+        rts                             ; Done
+
+;----------------------------------------------------------------------------
+; INFO: Returns mouse position and current button mask in the MOUSE_INFO
+; struct pointed to by ptr1. No return code required.
+;
+; We're cheating here to keep the code smaller: The first fields of the
+; mouse_info struct are identical to the mouse_pos struct, so we will just
+; call _mouse_pos to initialize the struct pointer and fill the position
+; fields.
+
+INFO:   jsr     POS
+
+; Fill in the button state
+
+        lda     Buttons
+        ldy     #MOUSE_INFO::BUTTONS
+        sta     (ptr1),y
+
+        rts
+
+;----------------------------------------------------------------------------
+; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
+; specific data in ptr1, and the ioctl code in A.
+; Must return an error code in a/x.
+;
+
+IOCTL:  lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
+        ldx     #>MOUSE_ERR_INV_IOCTL
+hlprts: rts
+
+;----------------------------------------------------------------------------
+; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
+; (so be careful). The routine MUST return carry set if the interrupt has been
+; 'handled' - which means that the interrupt source is gone. Otherwise it
+; MUST return carry clear.
+;
+
+IRQ:
+
+; Check for a pressed button and place the result into Buttons
+
+        ldx     #0
+        lda     TRIG0                   ; joystick #0 trigger
+        bne     @L0                     ; not pressed
+        ldx     #MOUSE_BTN_LEFT
+@L0:    stx     Buttons
+
+        lda     PORTA                   ; get joystick direction bits
+        and     #15                     ; clear joystick #1 bits
+        eor     #15
+        sta     Temp
+        clc
+        beq     hlprts                  ; no movement, do nothing
+
+        jsr     CHIDE
+
+; Check left/right
+
+        lda     Temp                    ; Read joystick #0
+        and     #(JOY::LEFT | JOY::RIGHT)
+        beq     @SkipX                  ;
+
+; We will cheat here and rely on the fact that either the left, OR the right
+; bit can be active
+
+        and     #JOY::RIGHT             ; Check RIGHT bit
+        bne     @Right
+        lda     #$FF
+        tax
+        bne     @AddX                   ; Branch always
+@Right: lda     #$01
+        ldx     #$00
+
+; Calculate the new X coordinate (--> a/y)
+
+@AddX:  add     XPos
+        tay                             ; Remember low byte
+        txa
+        adc     XPos+1
+        tax
+
+; Limit the X coordinate to the bounding box
+
+        cpy     XMin
+        sbc     XMin+1
+        bpl     @L1
+        ldy     XMin
+        ldx     XMin+1
+        jmp     @L2
+@L1:    txa
+
+        cpy     XMax
+        sbc     XMax+1
+        bmi     @L2
+        ldy     XMax
+        ldx     XMax+1
+@L2:    sty     XPos
+        stx     XPos+1
+
+; Move the mouse pointer to the new X pos
+
+        tya
+        jsr     CMOVEX
+
+; Calculate the Y movement vector
+
+@SkipX: lda     Temp                    ; Read joystick #0
+        and     #(JOY::UP | JOY::DOWN)  ; Check up/down
+        beq     @SkipY                  ;
+
+; We will cheat here and rely on the fact that either the up, OR the down
+; bit can be active
+
+        lsr     a
+        bcc     @Down
+        lda     #$FF
+        tax
+        bne     @AddY
+@Down:  lda     #$01
+        ldx     #$00
+
+; Calculate the new Y coordinate (--> a/y)
+
+@AddY:  add     YPos
+        tay                             ; Remember low byte
+        txa
+        adc     YPos+1
+        tax
+
+; Limit the Y coordinate to the bounding box
+
+        cpy     YMin
+        sbc     YMin+1
+        bpl     @L3
+        ldy     YMin
+        ldx     YMin+1
+        jmp     @L4
+@L3:    txa
+
+        cpy     YMax
+        sbc     YMax+1
+        bmi     @L4
+        ldy     YMax
+        ldx     YMax+1
+@L4:    sty     YPos
+        stx     YPos+1
+
+; Move the mouse pointer to the new X pos
+
+        tya
+        jsr     CMOVEY
+
+; Done
+
+@SkipY: lda     visible
+        beq     @Done
+
+        jsr     CSHOW
+
+@Done:  clc                             ; Interrupt not "handled"
+        rts
+
diff --git a/libsrc/atari/mouse_stat_stddrv.s b/libsrc/atari/mouse_stat_stddrv.s
new file mode 100644
index 000000000..18ebda9a2
--- /dev/null
+++ b/libsrc/atari/mouse_stat_stddrv.s
@@ -0,0 +1,22 @@
+;
+; Address of the static standard mouse driver
+;
+; Christian Groessler, 2014-01-02
+;
+; const void mouse_static_stddrv[];
+;
+
+        .export _mouse_static_stddrv
+        .ifdef  __ATARIXL__
+        .import _atrxjoy_mou
+        .else
+        .import _atrjoy_mou
+        .endif
+
+.rodata
+
+        .ifdef  __ATARIXL__
+_mouse_static_stddrv := _atrxjoy_mou
+        .else
+_mouse_static_stddrv := _atrjoy_mou
+        .endif
diff --git a/libsrc/atari/mouse_stddrv.s b/libsrc/atari/mouse_stddrv.s
new file mode 100644
index 000000000..443e53254
--- /dev/null
+++ b/libsrc/atari/mouse_stddrv.s
@@ -0,0 +1,18 @@
+;
+; Name of the standard mouse driver
+;
+; Christian Groessler, 2014-01-02
+;
+; const char mouse_stddrv[];
+;
+
+        .export _mouse_stddrv
+
+.rodata
+
+_mouse_stddrv:
+        .ifdef  __ATARIXL__
+        .asciiz "ATRXJOY.MOU"
+        .else
+        .asciiz "ATRJOY.MOU"
+        .endif
diff --git a/libsrc/atari/mul40.s b/libsrc/atari/mul40.s
index b94ab5c52..96235bf6c 100644
--- a/libsrc/atari/mul40.s
+++ b/libsrc/atari/mul40.s
@@ -6,7 +6,7 @@
 ; uses tmp4
 
         .importzp       tmp4
-        .export         mul40
+        .export         mul40,loc_tmp
 
 .proc   mul40
 

From 95bf72f12397a02f550e1c60983163171604ed4e Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 4 Jan 2014 16:37:59 +0100
Subject: [PATCH 04/31] fix typo

---
 asminc/atari.inc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/asminc/atari.inc b/asminc/atari.inc
index c60f6642d..49a125efb 100644
--- a/asminc/atari.inc
+++ b/asminc/atari.inc
@@ -318,7 +318,7 @@ APPMHI  = $0E           ;APPLICATIONS MEMORY HI LIMIT
 
 INTZBS  = $10           ;INTERRUPT HANDLER
 
-POKMSK  = $10           ;SYSTEM MASK FOR POKEY IRG ENABLE
+POKMSK  = $10           ;SYSTEM MASK FOR POKEY IRQ ENABLE
 BRKKEY  = $11           ;BREAK KEY FLAG
 RTCLOK  = $12           ;REAL TIME CLOCK (IN 16 MSEC UNITS>
 BUFADR  = $15           ;INDIRECT BUFFER ADDRESS REGISTER

From d742eeca9fd10158cdd08237975848917997b570 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 4 Jan 2014 18:28:24 +0100
Subject: [PATCH 05/31] Add drivers for ST mouse, Amiga mouse, and Atari
 trakball. Access routines taken from old mouse.s.

---
 libsrc/atari/mou/atrami.s   |   2 +
 libsrc/atari/mou/atrst.s    | 695 ++++++++++++++++++++++++++++++++++++
 libsrc/atari/mou/atrtrk.s   |   2 +
 libsrc/atari/mouse.s_       | 576 ------------------------------
 libsrc/atari/mouse_stddrv.s |   4 +-
 5 files changed, 701 insertions(+), 578 deletions(-)
 create mode 100644 libsrc/atari/mou/atrami.s
 create mode 100644 libsrc/atari/mou/atrst.s
 create mode 100644 libsrc/atari/mou/atrtrk.s
 delete mode 100644 libsrc/atari/mouse.s_

diff --git a/libsrc/atari/mou/atrami.s b/libsrc/atari/mou/atrami.s
new file mode 100644
index 000000000..21e1b4c81
--- /dev/null
+++ b/libsrc/atari/mou/atrami.s
@@ -0,0 +1,2 @@
+AMIGA_MOUSE = 1
+.include "atrst.s"
diff --git a/libsrc/atari/mou/atrst.s b/libsrc/atari/mou/atrst.s
new file mode 100644
index 000000000..1e7e77b3a
--- /dev/null
+++ b/libsrc/atari/mou/atrst.s
@@ -0,0 +1,695 @@
+;
+; Mouse driver for ST & Amiga mouses and Atari trakball.
+;
+; Original access routines: 05/07/2000 Freddy Offenga
+; Converted to driver: Christian Groessler, 2014-01-04
+;
+; Defines:
+;       AMIGA_MOUSE     -       builds Amiga mouse version
+;       TRAK_MOUSE      -       builds trakball version
+; If none of these defines are active, the ST mouse version
+; is being built.
+;
+
+        .include        "zeropage.inc"
+        .include        "mouse-kernel.inc"
+        .include        "atari.inc"
+
+        .macpack        generic
+
+.if .not ( .defined (AMIGA_MOUSE) .or .defined (TRAK_MOUSE))
+        ST_MOUSE = 1
+.endif
+
+; ------------------------------------------------------------------------
+; Header. Includes jump table
+
+.segment        "JUMPTABLE"
+
+HEADER:
+
+; Driver signature
+
+        .byte   $6d, $6f, $75           ; "mou"
+        .byte   MOUSE_API_VERSION       ; Mouse driver API version number
+
+; Library reference
+
+        .addr   $0000
+
+; Jump table
+
+        .addr   INSTALL
+        .addr   UNINSTALL
+        .addr   HIDE
+        .addr   SHOW
+        .addr   SETBOX
+        .addr   GETBOX
+        .addr   MOVE
+        .addr   BUTTONS
+        .addr   POS
+        .addr   INFO
+        .addr   IOCTL
+        .addr   IRQ
+
+; Mouse driver flags
+
+        .byte   MOUSE_FLAG_LATE_IRQ
+
+; Callback table, set by the kernel before INSTALL is called
+
+CHIDE:  jmp     $0000                   ; Hide the cursor
+CSHOW:  jmp     $0000                   ; Show the cursor
+CMOVEX: jmp     $0000                   ; Move the cursor to X coord
+CMOVEY: jmp     $0000                   ; Move the cursor to Y coord
+
+
+;----------------------------------------------------------------------------
+; Constants
+
+SCREEN_HEIGHT   = 191
+SCREEN_WIDTH    = 319
+
+.enum   JOY
+        UP      = $01
+        DOWN    = $02
+        LEFT    = $04
+        RIGHT   = $08
+.endenum
+
+;----------------------------------------------------------------------------
+; Global variables. The bounding box values are sorted so that they can be
+; written with the least effort in the SETBOX and GETBOX routines, so don't
+; reorder them.
+
+.bss
+
+Vars:
+YPos:           .res    2               ; Current mouse position, Y
+XPos:           .res    2               ; Current mouse position, X
+XMin:           .res    2               ; X1 value of bounding box
+YMin:           .res    2               ; Y1 value of bounding box
+XMax:           .res    2               ; X2 value of bounding box
+YMax:           .res    2               ; Y2 value of bounding box
+Buttons:        .res    1               ; Button mask
+
+XPosWrk:        .res    2
+YPosWrk:        .res    2
+
+OldT1:          .res    2
+visible:        .res    1
+
+.if .defined (AMIGA_MOUSE) .or .defined (ST_MOUSE)
+dumx:           .res    1
+dumy:           .res    1
+.endif
+
+.ifdef TRAK_MOUSE
+oldval:         .res    1
+.endif
+
+
+; Default values for some of the above variables
+
+.rodata
+
+.proc   DefVars
+        .word   (SCREEN_HEIGHT+1)/2     ; YPos
+        .word   (SCREEN_WIDTH+1)/2      ; XPos
+        .word   0                       ; XMin
+        .word   0                       ; YMin
+        .word   SCREEN_WIDTH            ; XMax
+        .word   SCREEN_HEIGHT           ; YMax
+        .byte   0                       ; Buttons
+.endproc
+
+.ifdef ST_MOUSE
+
+; ST mouse lookup table
+
+STTab:  .byte $FF,$01,$00,$01
+        .byte $00,$FF,$00,$01
+        .byte $01,$00,$FF,$00
+        .byte $01,$00,$01,$FF
+
+.endif
+
+.ifdef AMIGA_MOUSE
+
+; Amiga mouse lookup table
+
+AmiTab: .byte $FF,$01,$00,$FF
+        .byte $00,$FF,$FF,$01
+        .byte $01,$FF,$FF,$00
+        .byte $FF,$00,$01,$FF
+
+.endif
+
+.code
+
+;----------------------------------------------------------------------------
+; INSTALL routine. Is called after the driver is loaded into memory. If
+; possible, check if the hardware is present.
+; Must return an MOUSE_ERR_xx code in a/x.
+
+INSTALL:
+
+; Initialize variables. Just copy the default stuff over
+
+        ldx     #.sizeof(DefVars)-1
+@L1:    lda     DefVars,x
+        sta     Vars,x
+        dex
+        bpl     @L1
+
+; Be sure the mouse cursor is invisible and at the default location. We
+; need to do that here, because our mouse interrupt handler doesn't set the
+; mouse position if it hasn't changed.
+
+        sei
+        jsr     CHIDE
+        lda     XPos
+        sta     XPosWrk
+        ldx     XPos+1
+        stx     XPosWrk+1
+        jsr     CMOVEX
+        lda     YPos
+        sta     YPosWrk
+        ldx     YPos+1
+        stx     YPosWrk+1
+        jsr     CMOVEY
+        cli
+
+; install timer irq routine to poll mouse
+
+        lda     VTIMR1
+        sta     OldT1
+        lda     VTIMR1+1
+        sta     OldT1+1
+
+        php
+        sei
+        lda     #<T1Han
+        sta     VTIMR1
+        lda     #>T1Han
+        sta     VTIMR1+1
+        plp
+
+        lda     #%00000001
+        sta     AUDCTL
+
+        lda     #0
+        sta     AUDC1
+
+        lda     #15
+        sta     AUDF1
+        sta     STIMER
+
+        lda     POKMSK
+        ora     #%00000001              ; timer 1 enable
+        sta     POKMSK
+        sta     IRQEN
+
+; Done, return zero (= MOUSE_ERR_OK)
+
+        ldx     #$00
+        txa
+        rts
+
+;----------------------------------------------------------------------------
+; UNINSTALL routine. Is called before the driver is removed from memory.
+; No return code required (the driver is removed from memory on return).
+
+UNINSTALL:
+
+; uninstall timer irq routine
+
+        lda     POKMSK
+        and     #%11111110              ; timer 1 disable
+        sta     IRQEN
+        sta     POKMSK
+
+        php
+        sei
+        lda     OldT1
+        sta     VTIMR1
+        lda     OldT1+1
+        sta     VTIMR1+1
+        plp
+
+        ; fall thru...
+
+;----------------------------------------------------------------------------
+; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
+; a counter for calls to show/hide, and the driver entry point is only called
+; if the mouse is currently visible and should get hidden. For most drivers,
+; no special action is required besides hiding the mouse cursor.
+; No return code required.
+
+HIDE:   dec     visible
+        php
+        sei
+        jsr     CHIDE
+        plp
+        rts
+
+;----------------------------------------------------------------------------
+; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
+; a counter for calls to show/hide, and the driver entry point is only called
+; if the mouse is currently hidden and should become visible. For most drivers,
+; no special action is required besides enabling the mouse cursor.
+; No return code required.
+
+SHOW:   inc     visible
+        php
+        sei
+        jsr     CSHOW
+        plp
+        rts
+
+;----------------------------------------------------------------------------
+; SETBOX: Set the mouse bounding box. The parameters are passed as they come
+; from the C program, that is, a pointer to a mouse_box struct in a/x.
+; No checks are done if the mouse is currently inside the box, this is the job
+; of the caller. It is not necessary to validate the parameters, trust the
+; caller and save some code here. No return code required.
+
+SETBOX: sta     ptr1
+        stx     ptr1+1                  ; Save data pointer
+
+        ldy     #.sizeof (MOUSE_BOX)-1
+        php
+        sei
+
+@L1:    lda     (ptr1),y
+        sta     XMin,y
+        dey
+        bpl     @L1
+
+        plp
+        rts
+
+;----------------------------------------------------------------------------
+; GETBOX: Return the mouse bounding box. The parameters are passed as they
+; come from the C program, that is, a pointer to a mouse_box struct in a/x.
+
+GETBOX: sta     ptr1
+        stx     ptr1+1                  ; Save data pointer
+
+        ldy     #.sizeof (MOUSE_BOX)-1
+        php
+        sei
+
+@L1:    lda     XMin,y
+        sta     (ptr1),y
+        dey
+        bpl     @L1
+
+        plp
+        rts
+
+;----------------------------------------------------------------------------
+; MOVE: Move the mouse to a new position. The position is passed as it comes
+; from the C program, that is: X on the stack and Y in a/x. The C wrapper will
+; remove the parameter from the stack on return.
+; No checks are done if the new position is valid (within the bounding box or
+; the screen). No return code required.
+;
+
+MOVE:   php
+        sei                             ; No interrupts
+
+        pha
+        txa
+        pha
+
+        lda     visible
+        beq     @L01
+
+        jsr     CHIDE
+
+@L01:   pla
+        tax
+        pla
+
+        sta     YPos
+        sta     YPosWrk
+        stx     YPos+1                  ; New Y position
+        stx     YPosWrk+1
+        jsr     CMOVEY                  ; Set it
+
+        ldy     #$01
+        lda     (sp),y
+        sta     XPos+1
+        sta     XPosWrk+1
+        tax
+        dey
+        lda     (sp),y
+        sta     XPos                    ; New X position
+        sta     XPosWrk
+
+        jsr     CMOVEX                  ; Move the cursor
+
+        lda     visible
+        beq     @Ret
+        
+        jsr     CSHOW
+
+@Ret:   plp                             ; Restore interrupt flag
+        rts
+
+;----------------------------------------------------------------------------
+; BUTTONS: Return the button mask in a/x.
+
+BUTTONS:
+        lda     Buttons
+        ldx     #$00
+        rts
+
+;----------------------------------------------------------------------------
+; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
+; No return code required.
+
+POS:    ldy     #MOUSE_POS::XCOORD      ; Structure offset
+
+        php
+        sei                             ; Disable interrupts
+        lda     XPos                    ; Transfer the position
+        sta     (ptr1),y
+        lda     XPos+1
+        iny
+        sta     (ptr1),y
+        lda     YPos
+        iny
+        sta     (ptr1),y
+        lda     YPos+1
+        plp                             ; Restore interrupt flag
+
+        iny
+        sta     (ptr1),y                ; Store last byte
+
+        rts                             ; Done
+
+;----------------------------------------------------------------------------
+; INFO: Returns mouse position and current button mask in the MOUSE_INFO
+; struct pointed to by ptr1. No return code required.
+;
+; We're cheating here to keep the code smaller: The first fields of the
+; mouse_info struct are identical to the mouse_pos struct, so we will just
+; call _mouse_pos to initialize the struct pointer and fill the position
+; fields.
+
+INFO:   jsr     POS
+
+; Fill in the button state
+
+        lda     Buttons
+        ldy     #MOUSE_INFO::BUTTONS
+        sta     (ptr1),y
+
+        rts
+
+;----------------------------------------------------------------------------
+; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
+; specific data in ptr1, and the ioctl code in A.
+; Must return an error code in a/x.
+;
+
+IOCTL:  lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
+        ldx     #>MOUSE_ERR_INV_IOCTL
+        rts
+
+;----------------------------------------------------------------------------
+; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
+; (so be careful). The routine MUST return carry set if the interrupt has been
+; 'handled' - which means that the interrupt source is gone. Otherwise it
+; MUST return carry clear.
+;
+
+IRQ:
+
+; Check for a pressed button and place the result into Buttons
+
+        ldx     #0
+        lda     TRIG0                   ; joystick #0 trigger
+        bne     @L0                     ; not pressed
+        ldx     #MOUSE_BTN_LEFT
+@L0:    stx     Buttons
+
+; Update coordinates if needed
+
+        lda     XPosWrk
+        cmp     XPos
+        bne     @Update
+        lda     XPosWrk+1
+        cmp     XPos+1
+        bne     @Update
+        lda     YPosWrk
+        cmp     YPos
+        bne     @Update
+        lda     YPosWrk+1
+        cmp     YPos+1
+        beq     @Done
+
+@Update:ldx     visible
+        beq     @L1
+        jsr     CHIDE
+
+; Limit the X coordinate to the bounding box
+
+@L1:    lda     XPosWrk+1
+        ldy     XPosWrk
+        tax
+        cpy     XMin
+        sbc     XMin+1
+        bpl     @L2
+        ldy     XMin
+        ldx     XMin+1
+        jmp     @L3
+@L2:    txa
+
+        cpy     XMax
+        sbc     XMax+1
+        bmi     @L3
+        ldy     XMax
+        ldx     XMax+1
+@L3:    sty     XPos
+        stx     XPos+1
+
+        tya
+        jsr     CMOVEX
+
+; Limit the Y coordinate to the bounding box
+
+        lda     YPosWrk+1
+        ldy     YPosWrk
+        tax
+        cpy     YMin
+        sbc     YMin+1
+        bpl     @L4
+        ldy     YMin
+        ldx     YMin+1
+        jmp     @L5
+@L4:    txa
+
+        cpy     YMax
+        sbc     YMax+1
+        bmi     @L5
+        ldy     YMax
+        ldx     YMax+1
+@L5:    sty     YPos
+        stx     YPos+1
+
+        tya
+        jsr     CMOVEY
+
+        ldx     visible
+        beq     @Done
+
+        jsr     CSHOW
+
+@Done:  clc
+        rts
+
+;----------------------------------------------------------------------------
+; T1Han: Local IRQ routine to poll mouse
+;
+
+T1Han:  tya
+        pha
+        txa
+        pha     
+
+.ifdef DEBUG
+        lda     RANDOM
+        sta     COLBK
+.endif
+
+        lda     PORTA
+        tay
+
+.ifdef ST_MOUSE
+
+; ST mouse version
+
+        and     #%00000011
+        ora     dumx
+        tax
+        lda     STTab,x
+        bmi     nxst
+
+        beq     xist
+
+        dec     XPosWrk
+        lda     XPosWrk
+        cmp     #255
+        bne     nxst
+        dec     XPosWrk+1
+        jmp     nxst
+
+xist:   inc     XPosWrk
+        bne     nxst
+        inc     XPosWrk+1
+
+nxst:   tya
+        and     #%00001100
+        ora     dumy
+        tax
+        lda     STTab,x
+        bmi     nyst
+
+        bne     yst
+
+        dec     YPosWrk
+        lda     YPosWrk
+        cmp     #255
+        bne     nyst
+        dec     YPosWrk+1
+        jmp     nyst
+
+yst:    inc     YPosWrk
+        bne     nyst
+        inc     YPosWrk+1
+
+; store old readings
+
+nyst:   tya
+        and     #%00000011
+        asl
+        asl
+        sta     dumx
+        tya
+        and     #%00001100
+        lsr
+        lsr
+        sta     dumy
+
+.elseif .defined (AMIGA_MOUSE)
+
+; Amiga mouse version
+
+        lsr
+        and     #%00000101
+        ora     dumx
+        tax
+        lda     AmiTab,x
+        bmi     nxami
+
+        bne     xiami
+
+        dec     XPosWrk
+        lda     XPosWrk
+        cmp     #255
+        bne     nxami
+        dec     XPosWrk+1
+        jmp     nxami
+
+xiami:  inc     XPosWrk
+        bne     nxami
+        inc     XPosWrk+1
+
+nxami:  tya
+
+        and     #%00000101
+        ora     dumy
+        tax
+        lda     AmiTab,x
+        bmi     nyami
+
+        bne     yiami
+
+        dec     YPosWrk
+        lda     YPosWrk
+        cmp     #255
+        bne     nyami
+        dec     YPosWrk+1
+        jmp     nyami
+
+yiami:  inc     YPosWrk
+        bne     nyami
+        inc     YPosWrk+1
+
+; store old readings
+
+nyami:  tya
+        and     #%00001010
+        sta     dumx
+        tya
+        and     #%00000101
+        asl
+        sta     dumy
+
+.elseif .defined (TRAK_MOUSE)
+
+; trakball version
+
+        eor     oldval
+        and     #%00001000
+        beq     horiz
+
+        tya
+        and     #%00000100
+        beq     mmup
+
+        inc     YPosWrk
+        bne     horiz
+        inc     YPosWrk+1
+        bne     horiz
+
+mmup:   dec     YPosWrk
+        lda     YPosWrk
+        cmp     #255
+        bne     horiz
+        dec     YPosWrk+1
+
+horiz:  tya
+        eor     oldval
+        and     #%00000010
+        beq     mmexit
+
+        tya
+        and     #%00000001
+        beq     mmleft
+
+        inc     XPosWrk
+        bne     mmexit
+        inc     XPosWrk+1
+        bne     mmexit
+
+mmleft: dec     XPosWrk
+        lda     XPosWrk
+        cmp     #255
+        bne     mmexit
+        dec     XPosWrk+1
+
+mmexit: sty     oldval
+
+.endif
+
+        pla
+        tax
+        pla
+        tay
+        pla
+        rti
+
diff --git a/libsrc/atari/mou/atrtrk.s b/libsrc/atari/mou/atrtrk.s
new file mode 100644
index 000000000..699d12a0d
--- /dev/null
+++ b/libsrc/atari/mou/atrtrk.s
@@ -0,0 +1,2 @@
+TRAK_MOUSE = 1
+.include "atrst.s"
diff --git a/libsrc/atari/mouse.s_ b/libsrc/atari/mouse.s_
deleted file mode 100644
index 9c722b49c..000000000
--- a/libsrc/atari/mouse.s_
+++ /dev/null
@@ -1,576 +0,0 @@
-;--------------------------------------------------------------------
-; Atari 8-bit mouse routines -- 05/07/2000 Freddy Offenga
-; Some changes by Christian Groessler, Ullrich von Bassewitz
-;
-; The following devices are supported:
-; - Atari trak-ball
-; - ST mouse
-; - Amiga mouse
-;
-; Mouse checks are done in the timer 1 IRQ and the mouse arrow is
-; drawn in player 0 during the vertical blank
-;--------------------------------------------------------------------
-
-	.export		_mouse_init, _mouse_done, _mouse_box
-	.export 	_mouse_show, _mouse_hide, _mouse_move
-	.export 	_mouse_buttons, _mouse_pos, _mouse_info
-	.constructor	initmouse,27
-
-	.import 	popax
-	.importzp	ptr1
-
-	.include "atari.inc"
-
-TRAK_BALL	= 0	; device Atari trak-ball
-ST_MOUSE	= 1	; device ST mouse
-AMIGA_MOUSE	= 2	; device Amiga mouse
-MAX_TYPE	= 3	; first illegal device type
-
-; the default values force the mouse cursor inside the test screen (no access to border)
-defxmin = 48		; default x minimum
-defymin = 31		; default y minimum
-defxmax = 204		; default x maximum
-defymax = 211		; default y maximum
-
-pmsize	= 16		; y size pm shape
-
-xinit	= defxmin	; init. x pos.
-yinit	= defymin	; init. y pos.
-
-;--------------------------------------------------------------------
-; reserve memory for the mouse pointer
-
-initmouse:
-	lda	APPMHI+1
-	and	#%11111000	; make 2k aligned
-	sec
-	sbc	#%00001000	; reserve 2k
-	tax
-	adc	#3		; add 4 (C = 1)
-	sta	mouse_pm0
-	lda	#0
-	sta	APPMHI
-	stx	APPMHI+1
-	rts
-
-
-;--------------------------------------------------------------------
-; Initialize mouse routines
-; void __fastcall__ mouse_init (unsigned char type);
-
-_mouse_init:
-	cmp	#MAX_TYPE+1	; Check for a valid type
-	bcc	setup
-
-ifail:	lda	#0		; init. failed
-	tax
-	rts
-
-setup:	tax
-	lda	lvectab,x
-	sta	mouse_vec+1
-	lda	hvectab,x
-	sta	mouse_vec+2
-
-	jsr	pminit
-
-	lda	VTIMR1
-	sta	old_t1
-	lda	VTIMR1+1
-	sta	old_t1+1
-
-	lda	#<t1_vec
-	sta	VTIMR1
-	lda	#>t1_vec
-	sta	VTIMR1+1
-
-	lda	#%00000001
-	sta	AUDCTL
-
-	lda	#0
-	sta	AUDC1
-
-	lda	#15
-	sta	AUDF1
-	sta	STIMER
-
-	sei
-	lda	POKMSK
-	ora	#%00000001		; timer 1 enable
-	sta	POKMSK
-	sta	IRQEN
-	cli
-
-	lda	VVBLKI
-        sta     vbi_jmp+1
-	lda	VVBLKI+1
-        sta     vbi_jmp+2
-
-	lda	#6
-	ldy	#<vbi
-	ldx	#>vbi
-	jsr	SETVBV
-
-	lda	#$C0
-	sta	NMIEN
-
-	ldx	#0
-	lda	#1
-	sta    	mouse_off
-	rts
-
-;--------------------------------------------------------------------
-; Finish mouse routines
-; void mouse_done(void)
-
-_mouse_done:
-	sei
-	lda	POKMSK
-	and	#%11111110		; timer 1 disable
-	sta	IRQEN
-	sta	POKMSK
-	cli
-
-	lda	old_t1
-	sta	VTIMR1
-	lda	old_t1+1
-	sta	VTIMR1+1
-
-	lda	#$40
-	sta	NMIEN
-
-	lda	#6
-        ldy     vbi_jmp+1
-        ldx     vbi_jmp+2
-	jsr	SETVBV
-
-	ldx    	#0
-	stx	GRACTL
-	stx	HPOSP0
-	inx
-	stx    	mouse_off
-	rts
-
-;--------------------------------------------------------------------
-; Set mouse limits
-; void __fastcall__ mouse_box(int xmin, int ymin, int xmax, int ymax)
-
-_mouse_box:
-	sta	ymax
-        jsr     popax		; always ignore high byte
-	sta	xmax
-	jsr	popax
-	sta	ymin
-        jsr     popax
-	sta	xmin
-	rts
-
-;--------------------------------------------------------------------
-; Set mouse position
-; void __fastcall__ mouse_move(int xpos, int ypos)
-
-_mouse_move:
-	sta	mousey		; always ignore high byte
-	jsr	popax
-	sta	mousex
-	rts
-
-;--------------------------------------------------------------------
-; Show mouse arrow
-; void mouse_show(void)
-
-_mouse_show:
-	lda     mouse_off	; Already on?
-	beq	@L1
-       	dec	mouse_off
-@L1:	rts
-
-;--------------------------------------------------------------------
-; Hide mouse arrow
-; void mouse_hide(void)
-
-_mouse_hide:
-	inc	mouse_off
-	rts
-
-;--------------------------------------------------------------------
-; Ask mouse button
-; unsigned char mouse_buttons(void)
-
-_mouse_buttons:
-    	ldx	#0
-    	lda	STRIG0
-    	bne	nobut
-;   	lda	#14
-;???	sta	COLOR1
-    	lda	#1
-    	rts
-nobut:	txa
-    	rts
-
-;--------------------------------------------------------------------
-; Get the mouse position
-; void mouse_pos (struct mouse_pos* pos);
-
-_mouse_pos:
-	sta	ptr1
-	stx	ptr1+1			; Store argument pointer
-	ldy	#0
-	lda	mousex			; X position
-	sta	(ptr1),y
-	lda	#0
-	iny
-	sta	(ptr1),y
-	lda	mousey			; Y position
-	iny
-	sta	(ptr1),y
-	lda	#0
-	iny
-	sta	(ptr1),y
-    	rts
-
-;--------------------------------------------------------------------
-; Get the mouse position and button information
-; void mouse_info (struct mouse_info* info);
-
-_mouse_info:
-
-; We're cheating here to keep the code smaller: The first fields of the
-; mouse_info struct are identical to the mouse_pos struct, so we will just
-; call _mouse_pos to initialize the struct pointer and fill the position
-; fields.
-
-        jsr	_mouse_pos
-
-; Fill in the button state
-
-	jsr     _mouse_buttons		; Will not touch ptr1
-	ldy	#4
-	sta	(ptr1),y
-
-      	rts
-
-;--------------------------------------------------------------------
-; Atari trak-ball check, A,Y = 4-bit port value
-
-trak_check:
-    	eor	oldval
-    	and	#%00001000
-    	beq	horiz
-
-    	tya
-    	and	#%00000100
-	beq	mmup
-
-	inc	mousey
-	bne	horiz
-
-mmup:	dec	mousey
-
-horiz:	tya
-	eor	oldval
-	and	#%00000010
-	beq	mmexit
-
-	tya
-	and	#%00000001
-	beq	mmleft
-
-	inc	mousex
-	bne	mmexit
-
-mmleft: dec	mousex
-
-mmexit: sty	oldval
-	rts
-
-;--------------------------------------------------------------------
-; ST mouse check, A,Y = 4-bit port value
-
-st_check:
-	and	#%00000011
-	ora	dumx
-	tax
-	lda	sttab,x
-	bmi	nxst
-
-	beq	xist
-	dec	mousex		    ; 1 = left
-	bne	nxst
-xist:	inc	mousex		    ; 0 = right
-
-nxst:	tya
-	and	#%00001100
-	ora	dumy
-	tax
-	lda	sttab,x
-	bmi	nyst
-
-	bne	yst
-	dec	mousey		    ; 0 = up
-	bne	nyst
-yst:	inc	mousey		    ; 1 = down
-
-; store old readings
-
-nyst:	tya
-	and	#%00000011
-	asl
-	asl
-	sta	dumx
-	tya
-	and	#%00001100
-	lsr
-	lsr
-	sta	dumy
-	rts
-
-;--------------------------------------------------------------------
-; Amiga mouse check, A,Y = 4-bit port value
-
-amiga_check:
-
-	lsr
-	and	#%00000101
-	ora	dumx
-	tax
-	lda	amitab,x
-	bmi	nxami
-
-	bne	xiami
-	dec	mousex		    ; 0 = left
-	bne	nxami
-xiami:	inc	mousex		    ; 1 = right
-
-nxami:	tya
-
-	and	#%00000101
-	ora	dumy
-	tax
-	lda	amitab,x
-	bmi	nyami
-
-	bne	yiami
-	dec	mousey		    ; 0 =  up
-	bne	nyami
-yiami:	inc	mousey		    ; 1 = down
-
-; store old readings
-
-nyami:	tya
-	and	#%00001010
-	sta	dumx
-	tya
-	and	#%00000101
-	asl
-	sta	dumy
-	rts
-
-;--------------------------------------------------------------------
-; timer 1 IRQ routine - check mouse
-
-t1_vec: tya
-	pha
-	txa
-	pha	
-
-.ifdef DEBUG
-	lda	RANDOM
-	sta	COLBK		; debug
-.endif
-
-	lda	PORTA
-	tay
-
-mouse_vec:
-        jsr     st_check        ; will be modified; won't be ROMmable
-
-	pla
-	tax
-	pla
-	tay
-        pla
-	rti
-
-;--------------------------------------------------------------------
-; VBI - check mouse limits and display mouse arrow
-
-vbi:	lda	mousex
-	cmp	xmin
-        bcs     ok1             ; xmin <= mousex
-        lda     xmin
-	sta	mousex
-
-ok1:	lda	mousey
-	cmp	ymin
-        bcs     ok2             ; ymin <= mousey
-	lda	ymin
-	sta	mousey
-
-ok2:    lda     xmax
-        cmp     mousex
-        bcs     ok3             ; xmax >= mousex
-	lda	xmax
-	sta	mousex
-
-ok3:    lda     ymax
-        cmp     mousey
-        bcs     ok4             ; ymax >= mousey
-	lda	ymax
-	sta	mousey
-
-ok4:    jsr     clrpm
-
-	lda	mouse_off
-        beq     mon
-        lda     #0
-        sta     HPOSP0
-        beq     moff
-
-mon:    jsr     drwpm
-	lda	mousey
-	sta	omy
-
-        lda     #3
-moff:	sta	GRACTL
-
-vbi_jmp:
-        jmp     SYSVBV          ; will be modified; won't be ROMmable
-
-;--------------------------------------------------------------------
-; initialize mouse pm
-
-pminit:	lda	mouse_pm0
-	sta	mpatch1+2
-	sta	mpatch2+2
-	sta	mpatch3+2
-
-	ldx	#0
-	txa
-mpatch1:
-clpm:	sta	$1000,x		; will be patched
-	inx
-	bne	clpm
-
-	lda	mouse_pm0
-	sec
-	sbc	#4
-	sta	PMBASE
-
-	lda	#62
-	sta	SDMCTL
-
-        lda     #1
-	sta	GPRIOR
-
-	lda	#0
-	sta	PCOLR0
-        sta     SIZEP0
-	rts
-
-;--------------------------------------------------------------------
-; draw new mouse pm
-
-drwpm:	lda	mousex
-	sta	HPOSP0
-
-	lda	mousey
-	tax
-
-	ldy	#0
-fmp2:	lda	mskpm,y
-mpatch2:
-	sta	$1000,x		; will be patched
-	inx
-	iny
-	cpy	#pmsize
-	bne	fmp2
-	rts
-
-;--------------------------------------------------------------------
-; clear old mouse pm
-
-clrpm:	lda	omy
-        tax
-
-	ldy	#0
-	tya
-mpatch3:
-fmp1:	sta	$1000,x		; will be patched
-	inx
-	iny
-	cpy	#pmsize
-	bne	fmp1
-	rts
-
-;--------------------------------------------------------------------
-        .rodata
-
-; mouse arrow - pm shape
-
-mskpm:	.byte %00000000
-	.byte %10000000
-	.byte %11000000
-	.byte %11000000
-
-	.byte %11100000
-	.byte %11100000
-	.byte %11110000
-	.byte %11100000
-
-	.byte %11100000
-	.byte %00100000
-	.byte %00100000
-	.byte %00110000
-
-	.byte %00110000
-	.byte %00000000
-	.byte %00000000
-	.byte %00000000
-
-; ST mouse lookup table
-
-sttab:  .byte $FF,$01,$00,$01
-        .byte $00,$FF,$00,$01
-        .byte $01,$00,$FF,$00
-        .byte $01,$00,$01,$FF
-
-; Amiga mouse lookup table
-
-amitab: .byte $FF,$01,$00,$FF
-        .byte $00,$FF,$FF,$01
-        .byte $01,$FF,$FF,$00
-        .byte $FF,$00,$01,$FF
-
-; Device vectors
-
-lvectab:
-	.byte <trak_check, <st_check, <amiga_check
-hvectab:
-	.byte >trak_check, >st_check, >amiga_check
-
-; default values
-
-xmin:   .byte	defxmin
-ymin:   .byte	defymin
-xmax:	.byte	defxmax
-ymax:	.byte	defymax
-
-mousex:	.byte	xinit
-mousey:	.byte	yinit
-
-;--------------------------------------------------------------------
-	.bss
-
-; Misc. vars
-
-old_t1: .res 2		; old timer interrupt vector
-oldval: .res 1		; used by trakball routines
-dumx:	.res 1
-dumy:	.res 1
-omy:	.res 1		; old y pos
-
-mouse_off:
-	.res 1
-mouse_pm0:
-	.res 1
diff --git a/libsrc/atari/mouse_stddrv.s b/libsrc/atari/mouse_stddrv.s
index 443e53254..493c90d77 100644
--- a/libsrc/atari/mouse_stddrv.s
+++ b/libsrc/atari/mouse_stddrv.s
@@ -12,7 +12,7 @@
 
 _mouse_stddrv:
         .ifdef  __ATARIXL__
-        .asciiz "ATRXJOY.MOU"
+        .asciiz "ATRXST.MOU"
         .else
-        .asciiz "ATRJOY.MOU"
+        .asciiz "ATRST.MOU"
         .endif

From d614fc4e6483d03e4ce4544e692ce4351d8418c4 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 4 Jan 2014 20:05:57 +0100
Subject: [PATCH 06/31] When disabling interrupts, restore original interrupt
 flag afterwards.

---
 libsrc/atari/mou/atrjoy.s | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/libsrc/atari/mou/atrjoy.s b/libsrc/atari/mou/atrjoy.s
index 1a5afa71a..e1c745adf 100644
--- a/libsrc/atari/mou/atrjoy.s
+++ b/libsrc/atari/mou/atrjoy.s
@@ -122,6 +122,7 @@ INSTALL:
 ; need to do that here, because our mouse interrupt handler doesn't set the
 ; mouse position if it hasn't changed.
 
+        php
         sei
         jsr     CHIDE
         lda     XPos
@@ -130,7 +131,7 @@ INSTALL:
         lda     YPos
         ldx     YPos+1
         jsr     CMOVEY
-        cli
+        plp
 
 ; Done, return zero (= MOUSE_ERR_OK)
 
@@ -152,9 +153,10 @@ UNINSTALL       = HIDE                  ; Hide cursor on exit
 ; No return code required.
 
 HIDE:   dec     visible
+        php
         sei
         jsr     CHIDE
-        cli
+        plp
         rts
 
 ;----------------------------------------------------------------------------
@@ -165,9 +167,10 @@ HIDE:   dec     visible
 ; No return code required.
 
 SHOW:   inc     visible
+        php
         sei
         jsr     CSHOW
-        cli
+        plp
         rts
 
 ;----------------------------------------------------------------------------
@@ -181,6 +184,7 @@ SETBOX: sta     ptr1
         stx     ptr1+1                  ; Save data pointer
 
         ldy     #.sizeof (MOUSE_BOX)-1
+        php
         sei
 
 @L1:    lda     (ptr1),y
@@ -188,7 +192,7 @@ SETBOX: sta     ptr1
         dey
         bpl     @L1
 
-        cli
+        plp
         rts
 
 ;----------------------------------------------------------------------------
@@ -199,6 +203,7 @@ GETBOX: sta     ptr1
         stx     ptr1+1                  ; Save data pointer
 
         ldy     #.sizeof (MOUSE_BOX)-1
+        php
         sei
 
 @L1:    lda     XMin,y
@@ -206,7 +211,7 @@ GETBOX: sta     ptr1
         dey
         bpl     @L1
 
-        cli
+        plp
         rts
 
 ;----------------------------------------------------------------------------
@@ -217,7 +222,8 @@ GETBOX: sta     ptr1
 ; the screen). No return code required.
 ;
 
-MOVE:   sei                             ; No interrupts
+MOVE:   php
+        sei                             ; No interrupts
 
         pha
         txa
@@ -246,7 +252,7 @@ MOVE:   sei                             ; No interrupts
         
         jsr     CSHOW
 
-@Ret:   cli                             ; Allow interrupts
+@Ret:   plp                             ; Restore interrupt flag
         rts
 
 ;----------------------------------------------------------------------------
@@ -263,6 +269,7 @@ BUTTONS:
 
 POS:    ldy     #MOUSE_POS::XCOORD      ; Structure offset
 
+        php
         sei                             ; Disable interrupts
         lda     XPos                    ; Transfer the position
         sta     (ptr1),y
@@ -273,7 +280,7 @@ POS:    ldy     #MOUSE_POS::XCOORD      ; Structure offset
         iny
         sta     (ptr1),y
         lda     YPos+1
-        cli                             ; Enable interrupts
+        plp                             ; Restore interrupt flag
 
         iny
         sta     (ptr1),y                ; Store last byte

From 24198a9ebbfd5f6541a948f021c3a07aed2fedc0 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sun, 5 Jan 2014 11:09:01 +0100
Subject: [PATCH 07/31] small cleanup

---
 libsrc/atari/mcbdefault.s | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libsrc/atari/mcbdefault.s b/libsrc/atari/mcbdefault.s
index 91e920ac4..8d60a9ef7 100644
--- a/libsrc/atari/mcbdefault.s
+++ b/libsrc/atari/mcbdefault.s
@@ -83,7 +83,6 @@ show:
 
 ; Move the mouse cursor x position to the value in A/X.
 movex:
-        cpx #0
         cpx     #1
         ror     a
         lsr     a               ; convert to character position

From 09da71c5d913eba6e799c69c057fe6f67b6562fe Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sun, 5 Jan 2014 11:18:34 +0100
Subject: [PATCH 08/31] another small cleanup

---
 libsrc/atari/mcbdefault.s | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libsrc/atari/mcbdefault.s b/libsrc/atari/mcbdefault.s
index 8d60a9ef7..e68dba243 100644
--- a/libsrc/atari/mcbdefault.s
+++ b/libsrc/atari/mcbdefault.s
@@ -21,7 +21,6 @@
         .bss
 
 backup: .res    1
-helper: .res    2
 
 ; ------------------------------------------------------------------------
 

From c22b91e3c382e8bad5a62b9218b36d27d73f5c75 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 11 Jan 2014 01:05:13 +0100
Subject: [PATCH 09/31] align comment to the same column

---
 libsrc/atari/mcbdefault.s | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libsrc/atari/mcbdefault.s b/libsrc/atari/mcbdefault.s
index e68dba243..e1b78f033 100644
--- a/libsrc/atari/mcbdefault.s
+++ b/libsrc/atari/mcbdefault.s
@@ -43,19 +43,19 @@ _mouse_def_callbacks:
 
         .data
 
-cursor = 11             ; '+' screen code'
+cursor = 11                     ; '+' screen code'
 
 ; setcursor
 
 getcursor:
 column: ldy     #$00            ; Patched at runtime
-        lda     (scrptr),y         ; Patched at runtime
+        lda     (scrptr),y      ; Patched at runtime
         cmp     #cursor
         rts
 
 setcursor:
         lda     #cursor
-setscr: sta     (scrptr),y         ; Patched at runtime
+setscr: sta     (scrptr),y      ; Patched at runtime
         rts
 
 ; ------------------------------------------------------------------------

From c78d7a82fb1d83a5ee34abdafb70abcdc95cc35f Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 11 Jan 2014 01:33:59 +0100
Subject: [PATCH 10/31] use ST mouse as static standard driver, too

---
 libsrc/atari/mouse_stat_stddrv.s | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libsrc/atari/mouse_stat_stddrv.s b/libsrc/atari/mouse_stat_stddrv.s
index 18ebda9a2..55e29878b 100644
--- a/libsrc/atari/mouse_stat_stddrv.s
+++ b/libsrc/atari/mouse_stat_stddrv.s
@@ -8,15 +8,15 @@
 
         .export _mouse_static_stddrv
         .ifdef  __ATARIXL__
-        .import _atrxjoy_mou
+        .import _atrxst_mou
         .else
-        .import _atrjoy_mou
+        .import _atrst_mou
         .endif
 
 .rodata
 
         .ifdef  __ATARIXL__
-_mouse_static_stddrv := _atrxjoy_mou
+_mouse_static_stddrv := _atrxst_mou
         .else
-_mouse_static_stddrv := _atrjoy_mou
+_mouse_static_stddrv := _atrst_mou
         .endif

From 597c4a6b891a0145c223af00230fb66ffe83d469 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 11 Jan 2014 01:35:32 +0100
Subject: [PATCH 11/31] add comment

---
 libsrc/atari/mou/atrjoy.s | 2 ++
 libsrc/atari/mou/atrst.s  | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/libsrc/atari/mou/atrjoy.s b/libsrc/atari/mou/atrjoy.s
index e1c745adf..9915a4ff1 100644
--- a/libsrc/atari/mou/atrjoy.s
+++ b/libsrc/atari/mou/atrjoy.s
@@ -91,6 +91,8 @@ visible:        .res    1
 
 .rodata
 
+; (We use ".proc" because we want to define both a label and a scope.)
+
 .proc   DefVars
         .word   SCREEN_HEIGHT/2         ; YPos
         .word   SCREEN_WIDTH/2          ; XPos
diff --git a/libsrc/atari/mou/atrst.s b/libsrc/atari/mou/atrst.s
index 1e7e77b3a..72808ad16 100644
--- a/libsrc/atari/mou/atrst.s
+++ b/libsrc/atari/mou/atrst.s
@@ -113,6 +113,8 @@ oldval:         .res    1
 
 .rodata
 
+; (We use ".proc" because we want to define both a label and a scope.)
+
 .proc   DefVars
         .word   (SCREEN_HEIGHT+1)/2     ; YPos
         .word   (SCREEN_WIDTH+1)/2      ; XPos

From 7a682cf71a1cfda82015eb41a94b9d49986850d0 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Mon, 13 Jan 2014 00:45:35 +0100
Subject: [PATCH 12/31] fix some typos

---
 asminc/atari.inc | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/asminc/atari.inc b/asminc/atari.inc
index 49a125efb..da926af81 100644
--- a/asminc/atari.inc
+++ b/asminc/atari.inc
@@ -485,10 +485,10 @@ VKEYBD  = $0208         ;POKEY KEYBOARD IRQ VECTOR
 VSERIN  = $020A         ;POKEY SERIAL INPUT READY IRQ
 VSEROR  = $020C         ;POKEY SERIAL OUTPUT READY IRQ
 VSEROC  = $020E         ;POKEY SERIAL OUTPUT COMPLETE IRQ
-VTIMR1  = $0210         ;POKEY TIMER 1 IRG
-VTIMR2  = $0212         ;POKEY TIMER 2 IRG
-VTIMR4  = $0214         ;POKEY TIMER 4 IRG
-VIMIRQ  = $0216         ;IMMEDIATE IRG VECTOR
+VTIMR1  = $0210         ;POKEY TIMER 1 IRQ
+VTIMR2  = $0212         ;POKEY TIMER 2 IRQ
+VTIMR4  = $0214         ;POKEY TIMER 4 IRQ
+VIMIRQ  = $0216         ;IMMEDIATE IRQ VECTOR
 CDTMV1  = $0218         ;COUNT DOWN TIMER 1
 CDTMV2  = $021A         ;COUNT DOWN TIMER 2
 CDTMV3  = $021C         ;COUNT DOWN TIMER 3

From 77ba5b7e1c218b1294da14b8d9b1bb70337bfddb Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Tue, 14 Jan 2014 11:43:34 +0100
Subject: [PATCH 13/31] Fix timer IRQ hooking for atarixl target.

---
 libsrc/atari/libref.s               |  4 +-
 libsrc/atari/mou/atrst.s            | 49 ++++++++++++++---
 libsrc/atari/shadow_ram_timerirq1.s | 81 +++++++++++++++++++++++++++++
 3 files changed, 126 insertions(+), 8 deletions(-)
 create mode 100644 libsrc/atari/shadow_ram_timerirq1.s

diff --git a/libsrc/atari/libref.s b/libsrc/atari/libref.s
index e6b9df6a5..4f7cbbef6 100644
--- a/libsrc/atari/libref.s
+++ b/libsrc/atari/libref.s
@@ -8,11 +8,13 @@
 
 em_libref       := _exit
 joy_libref      := _exit
-mouse_libref    := _exit
 ser_libref      := atari_ser_libref
 .ifdef __ATARIXL__
         .import CIO_handler
 tgi_libref      := CIO_handler
+        .import set_VTIMR1_handler
+mouse_libref    := set_VTIMR1_handler
 .else
+mouse_libref    := _exit
 tgi_libref      := _exit
 .endif
diff --git a/libsrc/atari/mou/atrst.s b/libsrc/atari/mou/atrst.s
index 72808ad16..04580f31a 100644
--- a/libsrc/atari/mou/atrst.s
+++ b/libsrc/atari/mou/atrst.s
@@ -35,7 +35,7 @@ HEADER:
 
 ; Library reference
 
-        .addr   $0000
+libref: .addr   $0000
 
 ; Jump table
 
@@ -96,7 +96,6 @@ Buttons:        .res    1               ; Button mask
 XPosWrk:        .res    2
 YPosWrk:        .res    2
 
-OldT1:          .res    2
 visible:        .res    1
 
 .if .defined (AMIGA_MOUSE) .or .defined (ST_MOUSE)
@@ -108,11 +107,18 @@ dumy:           .res    1
 oldval:         .res    1
 .endif
 
+.ifndef __ATARIXL__
+OldT1:          .res    2
+.else
 
-; Default values for some of the above variables
+.data
+set_VTIMR1_handler:
+                .byte   $4C, 0, 0
+.endif
 
 .rodata
 
+; Default values for some of the above variables
 ; (We use ".proc" because we want to define both a label and a scope.)
 
 .proc   DefVars
@@ -182,7 +188,23 @@ INSTALL:
         jsr     CMOVEY
         cli
 
-; install timer irq routine to poll mouse
+; Install timer irq routine to poll mouse.
+
+.ifdef __ATARIXL__
+
+        ; Setup pointer to wrapper install/deinstall function.
+        lda     libref
+        sta     set_VTIMR1_handler+1
+        lda     libref+1
+        sta     set_VTIMR1_handler+2
+
+        ; Install my handler.
+        sec
+        lda     #<T1Han
+        ldx     #>T1Han
+        jsr     set_VTIMR1_handler
+
+.else
 
         lda     VTIMR1
         sta     OldT1
@@ -197,6 +219,8 @@ INSTALL:
         sta     VTIMR1+1
         plp
 
+.endif
+
         lda     #%00000001
         sta     AUDCTL
 
@@ -231,6 +255,13 @@ UNINSTALL:
         sta     IRQEN
         sta     POKMSK
 
+.ifdef __ATARIXL__
+
+        clc
+        jsr     set_VTIMR1_handler
+
+.else
+
         php
         sei
         lda     OldT1
@@ -239,6 +270,7 @@ UNINSTALL:
         sta     VTIMR1+1
         plp
 
+.endif
         ; fall thru...
 
 ;----------------------------------------------------------------------------
@@ -354,7 +386,7 @@ MOVE:   php
 
         lda     visible
         beq     @Ret
-        
+
         jsr     CSHOW
 
 @Ret:   plp                             ; Restore interrupt flag
@@ -520,7 +552,7 @@ IRQ:
 T1Han:  tya
         pha
         txa
-        pha     
+        pha
 
 .ifdef DEBUG
         lda     RANDOM
@@ -692,6 +724,9 @@ mmexit: sty     oldval
         tax
         pla
         tay
+.ifdef  __ATARIXL__
+        rts
+.else
         pla
         rti
-
+.endif
diff --git a/libsrc/atari/shadow_ram_timerirq1.s b/libsrc/atari/shadow_ram_timerirq1.s
new file mode 100644
index 000000000..f8a3e9b4d
--- /dev/null
+++ b/libsrc/atari/shadow_ram_timerirq1.s
@@ -0,0 +1,81 @@
+;
+; Atari XL shadow RAM timer IRQ #1 handler
+;
+; Christian Groessler, chris@groessler.org, 2014
+;
+
+;DEBUG           =       1
+
+.ifdef __ATARIXL__
+
+SHRAM_HANDLERS  =       1
+                .include        "atari.inc"
+                .include        "romswitch.inc"
+                .export         set_VTIMR1_handler
+
+
+.segment "LOWBSS"
+
+VTIMR1_handler: .res    3
+
+
+.segment "BSS"
+
+old_VTIMR1_handler:
+                .res    2
+
+
+.segment "LOWCODE"
+
+; timer interrupt handler:
+; disable ROM, call user handler, enable ROM again
+
+my_VTIMR1_handler:
+                disable_rom_quick
+                jsr     VTIMR1_handler
+                enable_rom_quick
+                pla
+                rti
+
+.segment "CODE"
+
+; install or remove VTIMR1 handler
+; input: CF - 0/1 for remove/install handler
+;        AX - pointer to handler (if CF=1)
+; registers destroyed
+
+set_VTIMR1_handler:
+
+                bcc     @remove
+
+; install vector
+
+                stx     VTIMR1_handler+2
+                sta     VTIMR1_handler+1        ; save passed vector in low memory
+                lda     #$4C                    ; "JMP" opcode
+                sta     VTIMR1_handler
+
+                lda     VTIMR1
+                sta     old_VTIMR1_handler
+                lda     VTIMR1+1
+                sta     old_VTIMR1_handler+1
+
+                lda     #<my_VTIMR1_handler
+                php
+                sei
+                sta     VTIMR1
+                lda     #>my_VTIMR1_handler
+                sta     VTIMR1+1
+                plp
+                rts
+
+@remove:        php
+                sei
+                lda     old_VTIMR1_handler
+                sta     VTIMR1
+                lda     old_VTIMR1_handler+1
+                sta     VTIMR1+1
+                plp
+                rts
+
+.endif  ; .ifdef __ATARIXL__

From 2be7c7c88d278855aea59b146ff73e10c7330027 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Tue, 14 Jan 2014 13:57:47 +0100
Subject: [PATCH 14/31] Always print the mouse cursor, even if coordinates
 haven't changed. This makes sure that the cursor is always visible, even if
 the program has written text to the screen (only valid for non-P/M mouse
 callbacks).

---
 libsrc/atari/mou/atrjoy.s | 11 ++---------
 libsrc/atari/mou/atrst.s  | 23 ++---------------------
 2 files changed, 4 insertions(+), 30 deletions(-)

diff --git a/libsrc/atari/mou/atrjoy.s b/libsrc/atari/mou/atrjoy.s
index 9915a4ff1..2c2d2c178 100644
--- a/libsrc/atari/mou/atrjoy.s
+++ b/libsrc/atari/mou/atrjoy.s
@@ -120,12 +120,8 @@ INSTALL:
         dex
         bpl     @L1
 
-; Be sure the mouse cursor is invisible and at the default location. We
-; need to do that here, because our mouse interrupt handler doesn't set the
-; mouse position if it hasn't changed.
+; Be sure the mouse cursor is invisible and at the default location.
 
-        php
-        sei
         jsr     CHIDE
         lda     XPos
         ldx     XPos+1
@@ -133,7 +129,6 @@ INSTALL:
         lda     YPos
         ldx     YPos+1
         jsr     CMOVEY
-        plp
 
 ; Done, return zero (= MOUSE_ERR_OK)
 
@@ -316,7 +311,7 @@ INFO:   jsr     POS
 
 IOCTL:  lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
         ldx     #>MOUSE_ERR_INV_IOCTL
-hlprts: rts
+        rts
 
 ;----------------------------------------------------------------------------
 ; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
@@ -339,8 +334,6 @@ IRQ:
         and     #15                     ; clear joystick #1 bits
         eor     #15
         sta     Temp
-        clc
-        beq     hlprts                  ; no movement, do nothing
 
         jsr     CHIDE
 
diff --git a/libsrc/atari/mou/atrst.s b/libsrc/atari/mou/atrst.s
index 04580f31a..03f9f0b53 100644
--- a/libsrc/atari/mou/atrst.s
+++ b/libsrc/atari/mou/atrst.s
@@ -170,11 +170,8 @@ INSTALL:
         dex
         bpl     @L1
 
-; Be sure the mouse cursor is invisible and at the default location. We
-; need to do that here, because our mouse interrupt handler doesn't set the
-; mouse position if it hasn't changed.
+; Be sure the mouse cursor is invisible and at the default location.
 
-        sei
         jsr     CHIDE
         lda     XPos
         sta     XPosWrk
@@ -186,7 +183,6 @@ INSTALL:
         ldx     YPos+1
         stx     YPosWrk+1
         jsr     CMOVEY
-        cli
 
 ; Install timer irq routine to poll mouse.
 
@@ -470,22 +466,7 @@ IRQ:
         ldx     #MOUSE_BTN_LEFT
 @L0:    stx     Buttons
 
-; Update coordinates if needed
-
-        lda     XPosWrk
-        cmp     XPos
-        bne     @Update
-        lda     XPosWrk+1
-        cmp     XPos+1
-        bne     @Update
-        lda     YPosWrk
-        cmp     YPos
-        bne     @Update
-        lda     YPosWrk+1
-        cmp     YPos+1
-        beq     @Done
-
-@Update:ldx     visible
+        ldx     visible
         beq     @L1
         jsr     CHIDE
 

From 09aa007191b79fcf90ceea8e6d6bd74ba8ec8f67 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Tue, 14 Jan 2014 14:00:17 +0100
Subject: [PATCH 15/31] Change display logic of mouse cursor (hide and show
 functions).

---
 libsrc/atari/mcbdefault.s | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/libsrc/atari/mcbdefault.s b/libsrc/atari/mcbdefault.s
index e1b78f033..11b7e07be 100644
--- a/libsrc/atari/mcbdefault.s
+++ b/libsrc/atari/mcbdefault.s
@@ -49,13 +49,12 @@ cursor = 11                     ; '+' screen code'
 
 getcursor:
 column: ldy     #$00            ; Patched at runtime
-        lda     (scrptr),y      ; Patched at runtime
-        cmp     #cursor
+        lda     (scrptr),y
         rts
 
 setcursor:
-        lda     #cursor
-setscr: sta     (scrptr),y      ; Patched at runtime
+column2:ldy     #$00            ; Patched at runtime
+        sta     (scrptr),y
         rts
 
 ; ------------------------------------------------------------------------
@@ -67,16 +66,19 @@ done:
 
 ; Hide the mouse cursor.
 hide:
-        jsr     getcursor       ; Cursor visible at current position?
-        bne     done            ; No, we're done
-        lda     backup          ; Get character at cursor position
-        jmp     setscr          ; Draw character
+        jsr     getcursor       ; Get character at cursor position
+        cmp     #cursor         ; "mouse" character
+        bne     overwr          ; no, probably program has overwritten it
+        lda     backup          ; 
+        jmp     setcursor       ; Draw character
+overwr: sta     backup
+        rts
 
 ; Show the mouse cursor.
 show:
         jsr     getcursor       ; Cursor visible at current position?
-        beq     done            ; Yes, we're done
         sta     backup          ; Save character at cursor position
+        lda     #cursor
         jmp     setcursor       ; Draw cursor
 
 
@@ -87,6 +89,7 @@ movex:
         lsr     a               ; convert to character position
         lsr     a
         sta     column+1
+        sta     column2+1
         rts
 
 ; Move the mouse cursor y position to the value in A/X.

From d61b8754fb522d815c60294fe10a8134de97df64 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 18 Jan 2014 00:02:29 +0100
Subject: [PATCH 16/31] Adapt to new mouse driver interface ('prep' and 'draw')

---
 libsrc/atari/mcbdefault.s | 11 ++++++++++
 libsrc/atari/mou/atrjoy.s | 32 ++++++++++------------------
 libsrc/atari/mou/atrst.s  | 44 +++++++++++++--------------------------
 3 files changed, 36 insertions(+), 51 deletions(-)

diff --git a/libsrc/atari/mcbdefault.s b/libsrc/atari/mcbdefault.s
index 11b7e07be..ac5056cf9 100644
--- a/libsrc/atari/mcbdefault.s
+++ b/libsrc/atari/mcbdefault.s
@@ -21,6 +21,7 @@
         .bss
 
 backup: .res    1
+visible:.res    1
 
 ; ------------------------------------------------------------------------
 
@@ -36,6 +37,8 @@ scrptr: .res    2
 _mouse_def_callbacks:
         .addr   hide
         .addr   show
+        .addr   prep
+        .addr   draw
         .addr   movex
         .addr   movey
 
@@ -66,6 +69,9 @@ done:
 
 ; Hide the mouse cursor.
 hide:
+        dec     visible
+
+prep:
         jsr     getcursor       ; Get character at cursor position
         cmp     #cursor         ; "mouse" character
         bne     overwr          ; no, probably program has overwritten it
@@ -76,6 +82,11 @@ overwr: sta     backup
 
 ; Show the mouse cursor.
 show:
+        inc     visible
+
+draw:
+        lda     visible
+        beq     done
         jsr     getcursor       ; Cursor visible at current position?
         sta     backup          ; Save character at cursor position
         lda     #cursor
diff --git a/libsrc/atari/mou/atrjoy.s b/libsrc/atari/mou/atrjoy.s
index 2c2d2c178..aa61615ad 100644
--- a/libsrc/atari/mou/atrjoy.s
+++ b/libsrc/atari/mou/atrjoy.s
@@ -50,6 +50,8 @@ HEADER:
 
 CHIDE:  jmp     $0000                   ; Hide the cursor
 CSHOW:  jmp     $0000                   ; Show the cursor
+CPREP:  jmp     $0000                   ; Prepare to move the cursor
+CDRAW:  jmp     $0000                   ; Draw the cursor
 CMOVEX: jmp     $0000                   ; Move the cursor to X coord
 CMOVEY: jmp     $0000                   ; Move the cursor to Y coord
 
@@ -85,7 +87,6 @@ Buttons:        .res    1               ; Button mask
 
 
 Temp:           .res    1               ; Temporary value used in the int handler
-visible:        .res    1
 
 ; Default values for above variables
 
@@ -120,9 +121,8 @@ INSTALL:
         dex
         bpl     @L1
 
-; Be sure the mouse cursor is invisible and at the default location.
+; Make sure the mouse cursor is at the default location.
 
-        jsr     CHIDE
         lda     XPos
         ldx     XPos+1
         jsr     CMOVEX
@@ -149,8 +149,7 @@ UNINSTALL       = HIDE                  ; Hide cursor on exit
 ; no special action is required besides hiding the mouse cursor.
 ; No return code required.
 
-HIDE:   dec     visible
-        php
+HIDE:   php
         sei
         jsr     CHIDE
         plp
@@ -163,8 +162,7 @@ HIDE:   dec     visible
 ; no special action is required besides enabling the mouse cursor.
 ; No return code required.
 
-SHOW:   inc     visible
-        php
+SHOW:   php
         sei
         jsr     CSHOW
         plp
@@ -225,7 +223,7 @@ MOVE:   php
         pha
         txa
         pha
-        jsr     CHIDE
+        jsr     CPREP
         pla
         tax
         pla
@@ -241,15 +239,11 @@ MOVE:   php
         dey
         lda     (sp),y
         sta     XPos                    ; New X position
-
         jsr     CMOVEX                  ; Move the cursor
 
-        lda     visible
-        beq     @Ret
-        
-        jsr     CSHOW
+        jsr     CDRAW
 
-@Ret:   plp                             ; Restore interrupt flag
+        plp                             ; Restore interrupt flag
         rts
 
 ;----------------------------------------------------------------------------
@@ -335,7 +329,7 @@ IRQ:
         eor     #15
         sta     Temp
 
-        jsr     CHIDE
+        jsr     CPREP
 
 ; Check left/right
 
@@ -435,11 +429,7 @@ IRQ:
 
 ; Done
 
-@SkipY: lda     visible
-        beq     @Done
-
-        jsr     CSHOW
-
-@Done:  clc                             ; Interrupt not "handled"
+@SkipY: jsr     CDRAW
+        clc                             ; Interrupt not "handled"
         rts
 
diff --git a/libsrc/atari/mou/atrst.s b/libsrc/atari/mou/atrst.s
index 03f9f0b53..116cecee2 100644
--- a/libsrc/atari/mou/atrst.s
+++ b/libsrc/atari/mou/atrst.s
@@ -60,6 +60,8 @@ libref: .addr   $0000
 
 CHIDE:  jmp     $0000                   ; Hide the cursor
 CSHOW:  jmp     $0000                   ; Show the cursor
+CPREP:  jmp     $0000                   ; Prepare to move the cursor
+CDRAW:  jmp     $0000                   ; Draw the cursor
 CMOVEX: jmp     $0000                   ; Move the cursor to X coord
 CMOVEY: jmp     $0000                   ; Move the cursor to Y coord
 
@@ -96,8 +98,6 @@ Buttons:        .res    1               ; Button mask
 XPosWrk:        .res    2
 YPosWrk:        .res    2
 
-visible:        .res    1
-
 .if .defined (AMIGA_MOUSE) .or .defined (ST_MOUSE)
 dumx:           .res    1
 dumy:           .res    1
@@ -170,9 +170,8 @@ INSTALL:
         dex
         bpl     @L1
 
-; Be sure the mouse cursor is invisible and at the default location.
+; Make sure the mouse cursor is at the default location.
 
-        jsr     CHIDE
         lda     XPos
         sta     XPosWrk
         ldx     XPos+1
@@ -276,8 +275,7 @@ UNINSTALL:
 ; no special action is required besides hiding the mouse cursor.
 ; No return code required.
 
-HIDE:   dec     visible
-        php
+HIDE:   php
         sei
         jsr     CHIDE
         plp
@@ -290,8 +288,7 @@ HIDE:   dec     visible
 ; no special action is required besides enabling the mouse cursor.
 ; No return code required.
 
-SHOW:   inc     visible
-        php
+SHOW:   php
         sei
         jsr     CSHOW
         plp
@@ -352,13 +349,8 @@ MOVE:   php
         pha
         txa
         pha
-
-        lda     visible
-        beq     @L01
-
-        jsr     CHIDE
-
-@L01:   pla
+        jsr     CPREP
+        pla
         tax
         pla
 
@@ -377,15 +369,11 @@ MOVE:   php
         lda     (sp),y
         sta     XPos                    ; New X position
         sta     XPosWrk
-
         jsr     CMOVEX                  ; Move the cursor
 
-        lda     visible
-        beq     @Ret
+        jsr     CDRAW
 
-        jsr     CSHOW
-
-@Ret:   plp                             ; Restore interrupt flag
+        plp                             ; Restore interrupt flag
         rts
 
 ;----------------------------------------------------------------------------
@@ -466,13 +454,11 @@ IRQ:
         ldx     #MOUSE_BTN_LEFT
 @L0:    stx     Buttons
 
-        ldx     visible
-        beq     @L1
-        jsr     CHIDE
+        jsr     CPREP
 
 ; Limit the X coordinate to the bounding box
 
-@L1:    lda     XPosWrk+1
+        lda     XPosWrk+1
         ldy     XPosWrk
         tax
         cpy     XMin
@@ -518,14 +504,12 @@ IRQ:
         tya
         jsr     CMOVEY
 
-        ldx     visible
-        beq     @Done
+        jsr     CDRAW
 
-        jsr     CSHOW
-
-@Done:  clc
+        clc
         rts
 
+
 ;----------------------------------------------------------------------------
 ; T1Han: Local IRQ routine to poll mouse
 ;

From 6026776460e193551f9d1e186e3d8d4a9cf459c0 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sun, 5 Jan 2014 16:43:17 +0100
Subject: [PATCH 17/31] Trakball mouse driver

---
 libsrc/atari/mou/atrtt.s | 481 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 481 insertions(+)
 create mode 100644 libsrc/atari/mou/atrtt.s

diff --git a/libsrc/atari/mou/atrtt.s b/libsrc/atari/mou/atrtt.s
new file mode 100644
index 000000000..16ed32602
--- /dev/null
+++ b/libsrc/atari/mou/atrtt.s
@@ -0,0 +1,481 @@
+;
+; Mouse driver for Atari Trakball
+;
+; Christian Groessler, 2014-01-05
+;
+
+        .include        "zeropage.inc"
+        .include        "mouse-kernel.inc"
+        .include        "atari.inc"
+
+        .macpack        generic
+
+; ------------------------------------------------------------------------
+; Header. Includes jump table
+
+.segment        "JUMPTABLE"
+
+HEADER:
+
+; Driver signature
+
+        .byte   $6d, $6f, $75           ; "mou"
+        .byte   MOUSE_API_VERSION       ; Mouse driver API version number
+
+; Library reference
+
+        .addr   $0000
+
+; Jump table
+
+        .addr   INSTALL
+        .addr   UNINSTALL
+        .addr   HIDE
+        .addr   SHOW
+        .addr   SETBOX
+        .addr   GETBOX
+        .addr   MOVE
+        .addr   BUTTONS
+        .addr   POS
+        .addr   INFO
+        .addr   IOCTL
+        .addr   IRQ
+
+; Mouse driver flags
+
+        .byte   MOUSE_FLAG_LATE_IRQ
+
+; Callback table, set by the kernel before INSTALL is called
+
+CHIDE:  jmp     $0000                   ; Hide the cursor
+CSHOW:  jmp     $0000                   ; Show the cursor
+CMOVEX: jmp     $0000                   ; Move the cursor to X coord
+CMOVEY: jmp     $0000                   ; Move the cursor to Y coord
+
+
+;----------------------------------------------------------------------------
+; Constants
+
+SCREEN_HEIGHT   = 191
+SCREEN_WIDTH    = 319
+
+.enum   JOY
+        UP      = $01
+        DOWN    = $02
+        LEFT    = $04
+        RIGHT   = $08
+.endenum
+
+;----------------------------------------------------------------------------
+; Global variables. The bounding box values are sorted so that they can be
+; written with the least effort in the SETBOX and GETBOX routines, so don't
+; reorder them.
+
+.bss
+
+Vars:
+YPos:           .res    2               ; Current mouse position, Y
+XPos:           .res    2               ; Current mouse position, X
+XMin:           .res    2               ; X1 value of bounding box
+YMin:           .res    2               ; Y1 value of bounding box
+XMax:           .res    2               ; X2 value of bounding box
+YMax:           .res    2               ; Y2 value of bounding box
+Buttons:        .res    1               ; Button mask
+
+
+visible:        .res    1
+
+; Default values for above variables
+
+.rodata
+
+.proc   DefVars
+        .word   SCREEN_HEIGHT/2         ; YPos
+        .word   SCREEN_WIDTH/2          ; XPos
+        .word   0                       ; XMin
+        .word   0                       ; YMin
+        .word   SCREEN_WIDTH            ; XMax
+        .word   SCREEN_HEIGHT           ; YMax
+        .byte   0                       ; Buttons
+.endproc
+
+.code
+
+;----------------------------------------------------------------------------
+; INSTALL routine. Is called after the driver is loaded into memory. If
+; possible, check if the hardware is present.
+; Must return an MOUSE_ERR_xx code in a/x.
+
+INSTALL:
+
+; Initialize variables. Just copy the default stuff over
+
+        ldx     #.sizeof(DefVars)-1
+@L1:    lda     DefVars,x
+        sta     Vars,x
+        dex
+        bpl     @L1
+
+; Be sure the mouse cursor is invisible and at the default location. We
+; need to do that here, because our mouse interrupt handler doesn't set the
+; mouse position if it hasn't changed.
+
+        php
+        sei
+        jsr     CHIDE
+        lda     XPos
+        ldx     XPos+1
+        jsr     CMOVEX
+        lda     YPos
+        ldx     YPos+1
+        jsr     CMOVEY
+        plp
+
+; Done, return zero (= MOUSE_ERR_OK)
+
+        ldx     #$00
+        txa
+        rts
+
+;----------------------------------------------------------------------------
+; UNINSTALL routine. Is called before the driver is removed from memory.
+; No return code required (the driver is removed from memory on return).
+
+UNINSTALL       = HIDE                  ; Hide cursor on exit
+
+;----------------------------------------------------------------------------
+; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
+; a counter for calls to show/hide, and the driver entry point is only called
+; if the mouse is currently visible and should get hidden. For most drivers,
+; no special action is required besides hiding the mouse cursor.
+; No return code required.
+
+HIDE:   dec     visible
+        php
+        sei
+        jsr     CHIDE
+        plp
+        rts
+
+;----------------------------------------------------------------------------
+; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
+; a counter for calls to show/hide, and the driver entry point is only called
+; if the mouse is currently hidden and should become visible. For most drivers,
+; no special action is required besides enabling the mouse cursor.
+; No return code required.
+
+SHOW:   inc     visible
+        php
+        sei
+        jsr     CSHOW
+        plp
+        rts
+
+;----------------------------------------------------------------------------
+; SETBOX: Set the mouse bounding box. The parameters are passed as they come
+; from the C program, that is, a pointer to a mouse_box struct in a/x.
+; No checks are done if the mouse is currently inside the box, this is the job
+; of the caller. It is not necessary to validate the parameters, trust the
+; caller and save some code here. No return code required.
+
+SETBOX: sta     ptr1
+        stx     ptr1+1                  ; Save data pointer
+
+        ldy     #.sizeof (MOUSE_BOX)-1
+        php
+        sei
+
+@L1:    lda     (ptr1),y
+        sta     XMin,y
+        dey
+        bpl     @L1
+
+        plp
+        rts
+
+;----------------------------------------------------------------------------
+; GETBOX: Return the mouse bounding box. The parameters are passed as they
+; come from the C program, that is, a pointer to a mouse_box struct in a/x.
+
+GETBOX: sta     ptr1
+        stx     ptr1+1                  ; Save data pointer
+
+        ldy     #.sizeof (MOUSE_BOX)-1
+        php
+        sei
+
+@L1:    lda     XMin,y
+        sta     (ptr1),y
+        dey
+        bpl     @L1
+
+        plp
+        rts
+
+;----------------------------------------------------------------------------
+; MOVE: Move the mouse to a new position. The position is passed as it comes
+; from the C program, that is: X on the stack and Y in a/x. The C wrapper will
+; remove the parameter from the stack on return.
+; No checks are done if the new position is valid (within the bounding box or
+; the screen). No return code required.
+;
+
+MOVE:   php
+        sei                             ; No interrupts
+
+        pha
+        lda     visible
+        beq     @nohide
+        txa
+        pha
+        jsr     CHIDE
+        pla
+        tax
+@nohide:pla
+
+        sta     YPos
+        stx     YPos+1                  ; New Y position
+        jsr     CMOVEY                  ; Set it
+
+        ldy     #$01
+        lda     (sp),y
+        sta     XPos+1
+        tax
+        dey
+        lda     (sp),y
+        sta     XPos                    ; New X position
+
+        jsr     CMOVEX                  ; Move the cursor
+
+        lda     visible
+        beq     @Ret
+        
+        jsr     CSHOW
+
+@Ret:   plp                             ; Restore interrupt flag
+        rts
+
+;----------------------------------------------------------------------------
+; BUTTONS: Return the button mask in a/x.
+
+BUTTONS:
+        lda     Buttons
+        ldx     #$00
+        rts
+
+;----------------------------------------------------------------------------
+; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
+; No return code required.
+
+POS:    ldy     #MOUSE_POS::XCOORD      ; Structure offset
+
+        php
+        sei                             ; Disable interrupts
+        lda     XPos                    ; Transfer the position
+        sta     (ptr1),y
+        lda     XPos+1
+        iny
+        sta     (ptr1),y
+        lda     YPos
+        iny
+        sta     (ptr1),y
+        lda     YPos+1
+        plp                             ; Restore interrupt flag
+
+        iny
+        sta     (ptr1),y                ; Store last byte
+
+        rts                             ; Done
+
+;----------------------------------------------------------------------------
+; INFO: Returns mouse position and current button mask in the MOUSE_INFO
+; struct pointed to by ptr1. No return code required.
+;
+; We're cheating here to keep the code smaller: The first fields of the
+; mouse_info struct are identical to the mouse_pos struct, so we will just
+; call _mouse_pos to initialize the struct pointer and fill the position
+; fields.
+
+INFO:   jsr     POS
+
+; Fill in the button state
+
+        lda     Buttons
+        ldy     #MOUSE_INFO::BUTTONS
+        sta     (ptr1),y
+
+        rts
+
+;----------------------------------------------------------------------------
+; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
+; specific data in ptr1, and the ioctl code in A.
+; Must return an error code in a/x.
+;
+
+IOCTL:  lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
+        ldx     #>MOUSE_ERR_INV_IOCTL
+        rts
+
+;----------------------------------------------------------------------------
+; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
+; (so be careful). The routine MUST return carry set if the interrupt has been
+; 'handled' - which means that the interrupt source is gone. Otherwise it
+; MUST return carry clear.
+;
+
+IRQ:
+
+; Check for a pressed button and place the result into Buttons
+
+        ldx     #0
+        stx     XPos+1
+        stx     YPos+1
+        lda     TRIG0                   ; joystick #0 trigger
+        bne     @L00                    ; not pressed
+        ldx     #MOUSE_BTN_LEFT
+@L00:   stx     Buttons
+
+        lda     PORTA                   ; get other buttons
+        eor     #255
+        tax
+        and     #4
+        beq     @L01
+        lda     #MOUSE_BTN_LEFT
+        ora     Buttons
+        sta     Buttons
+@L01:   txa
+        and     #8
+        beq     @L02
+        lda     #MOUSE_BTN_RIGHT
+        ora     Buttons
+        sta     Buttons
+
+@L02:   lda     visible
+        beq     @L03
+        jsr     CHIDE
+
+; Get cursor position
+; -------------------
+; The touchpad is read thru the paddle potentiometers. The possible
+; values are 1..228. Since the maximum value is less than the X
+; dimension we have to "stretch" this value. In order to use only
+; divisions by powers of two, we use the following appoximation:
+; 320/227 = 1.4096
+; 1+1/2-1/8+1/32 = 1.4062
+; For Y we subtract 1/8 of it to get in the YMax ballpark.
+; 228-228/8=199.5
+; A small area in the Y dimension of the touchpad isn't used with
+; this approximation. The Y value is inverted, (0,0) is the bottom
+; left corner of the touchpad.
+
+; X
+
+@L03:   ldx     PADDL0                  ; get X postion
+        dex                             ; decrement, since it's 1-based
+        stx     XPos
+        txa
+        lsr     a
+        tax
+        clc
+        adc     XPos
+        sta     XPos
+        bcc     @L04
+        inc     XPos+1
+@L04:   txa
+        lsr     a                       ; port value / 4
+        lsr     a                       ; port value / 8
+        tax
+        sec
+        lda     XPos
+        stx     XPos
+        sbc     XPos
+        sta     XPos
+        bcs     @L05
+        dec     XPos+1
+@L05:   txa
+        lsr     a                       ; port value / 16
+        lsr     a                       ; port value / 32
+        clc
+        adc     XPos
+        sta     XPos
+        bcc     @L06
+        inc     XPos+1
+
+@L06:   tay
+        lda     XPos+1
+        tax
+
+; Limit the X coordinate to the bounding box
+
+        cpy     XMin
+        sbc     XMin+1
+        bpl     @L07
+        ldy     XMin
+        ldx     XMin+1
+        jmp     @L08
+@L07:   txa
+
+        cpy     XMax
+        sbc     XMax+1
+        bmi     @L08
+        ldy     XMax
+        ldx     XMax+1
+@L08:   sty     XPos
+        stx     XPos+1
+
+; Move the mouse pointer to the new X pos
+
+        tya
+        jsr     CMOVEX
+
+; Y
+
+        ldx     PADDL1                  ; get Y postion
+        dex                             ; decrement, since it's 1-based
+        stx     YPos
+        lda     #228
+        sec
+        sbc     YPos                    ; invert value
+        tax
+        lsr     a
+        lsr     a
+        lsr     a
+        sta     YPos
+        txa
+        sec
+        sbc     YPos
+        sta     YPos
+        tay
+        lda     YPos+1
+        tax
+
+; Limit the Y coordinate to the bounding box
+
+        cpy     YMin
+        sbc     YMin+1
+        bpl     @L09
+        ldy     YMin
+        ldx     YMin+1
+        jmp     @L10
+@L09:   txa
+
+        cpy     YMax
+        sbc     YMax+1
+        bmi     @L10
+        ldy     YMax
+        ldx     YMax+1
+@L10:   sty     YPos
+        stx     YPos+1
+
+; Move the mouse pointer to the new X pos
+
+        tya
+        jsr     CMOVEY
+
+        lda     visible
+        beq     @L11
+        jsr     CSHOW
+
+@L11:   clc                             ; Interrupt not "handled"
+        rts
+

From 97d512a1e180c1c9faa64b04d42e59cc1c9ac1d7 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Mon, 6 Jan 2014 13:36:09 +0100
Subject: [PATCH 18/31] Detect pen button. If we read 228 for X or Y position,
 don't change cursor position, since the pen was probably lifted from the pad.

---
 libsrc/atari/mou/atrtt.s | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/libsrc/atari/mou/atrtt.s b/libsrc/atari/mou/atrtt.s
index 16ed32602..5bf9e38a2 100644
--- a/libsrc/atari/mou/atrtt.s
+++ b/libsrc/atari/mou/atrtt.s
@@ -330,15 +330,12 @@ IRQ:
         ldx     #0
         stx     XPos+1
         stx     YPos+1
-        lda     TRIG0                   ; joystick #0 trigger
-        bne     @L00                    ; not pressed
-        ldx     #MOUSE_BTN_LEFT
-@L00:   stx     Buttons
+        stx     Buttons
 
         lda     PORTA                   ; get other buttons
         eor     #255
         tax
-        and     #4
+        and     #5			; pen button and left button are mapped to left mouse button
         beq     @L01
         lda     #MOUSE_BTN_LEFT
         ora     Buttons
@@ -350,7 +347,18 @@ IRQ:
         ora     Buttons
         sta     Buttons
 
-@L02:   lda     visible
+; If we read 228 for X or Y positions, we assume the user has lifted the pen
+; and don't change the cursor position.
+
+@L02:   lda	PADDL0
+	cmp	#228
+	beq	@Dont
+	lda	PADDL1
+	cmp	#228
+	bne	@Do
+@Dont:  jmp	@Done
+
+@Do:	lda     visible
         beq     @L03
         jsr     CHIDE
 
@@ -473,9 +481,9 @@ IRQ:
         jsr     CMOVEY
 
         lda     visible
-        beq     @L11
+        beq     @Done
         jsr     CSHOW
 
-@L11:   clc                             ; Interrupt not "handled"
+@Done:  clc                             ; Interrupt not "handled"
         rts
 

From ecd10e632a65ea0cc256ed2797de15e6f776e231 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Wed, 8 Jan 2014 02:28:39 +0100
Subject: [PATCH 19/31] some comment fixes

---
 libsrc/atari/mou/atrtt.s | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libsrc/atari/mou/atrtt.s b/libsrc/atari/mou/atrtt.s
index 5bf9e38a2..dc402db26 100644
--- a/libsrc/atari/mou/atrtt.s
+++ b/libsrc/atari/mou/atrtt.s
@@ -1,5 +1,5 @@
 ;
-; Mouse driver for Atari Trakball
+; Mouse driver for Atari Touch Tablet
 ;
 ; Christian Groessler, 2014-01-05
 ;
@@ -364,7 +364,7 @@ IRQ:
 
 ; Get cursor position
 ; -------------------
-; The touchpad is read thru the paddle potentiometers. The possible
+; The touch pad is read thru the paddle potentiometers. The possible
 ; values are 1..228. Since the maximum value is less than the X
 ; dimension we have to "stretch" this value. In order to use only
 ; divisions by powers of two, we use the following appoximation:

From 1fd7c2b3182e83242cc030aed5df2cfc2a4353b3 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 11 Jan 2014 01:49:21 +0100
Subject: [PATCH 20/31] fix bug where the high byte of X and Y positions were
 destroyed of the user had lifted the pen

---
 libsrc/atari/mou/atrtt.s | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/libsrc/atari/mou/atrtt.s b/libsrc/atari/mou/atrtt.s
index dc402db26..9d2dac1c9 100644
--- a/libsrc/atari/mou/atrtt.s
+++ b/libsrc/atari/mou/atrtt.s
@@ -89,6 +89,8 @@ visible:        .res    1
 
 .rodata
 
+; (We use ".proc" because we want to define both a label and a scope.)
+
 .proc   DefVars
         .word   SCREEN_HEIGHT/2         ; YPos
         .word   SCREEN_WIDTH/2          ; XPos
@@ -328,14 +330,12 @@ IRQ:
 ; Check for a pressed button and place the result into Buttons
 
         ldx     #0
-        stx     XPos+1
-        stx     YPos+1
         stx     Buttons
 
         lda     PORTA                   ; get other buttons
         eor     #255
         tax
-        and     #5			; pen button and left button are mapped to left mouse button
+        and     #5                      ; pen button and left button are mapped to left mouse button
         beq     @L01
         lda     #MOUSE_BTN_LEFT
         ora     Buttons
@@ -350,18 +350,22 @@ IRQ:
 ; If we read 228 for X or Y positions, we assume the user has lifted the pen
 ; and don't change the cursor position.
 
-@L02:   lda	PADDL0
-	cmp	#228
-	beq	@Dont
-	lda	PADDL1
-	cmp	#228
-	bne	@Do
-@Dont:  jmp	@Done
+@L02:   lda     PADDL0
+        cmp     #228
+        beq     @Dont
+        lda     PADDL1
+        cmp     #228
+        bne     @Do
+@Dont:  jmp     @Done
 
-@Do:	lda     visible
+@Do:    lda     visible
         beq     @L03
         jsr     CHIDE
 
+@L03:   ldx     #0
+        stx     XPos+1
+        stx     YPos+1
+
 ; Get cursor position
 ; -------------------
 ; The touch pad is read thru the paddle potentiometers. The possible
@@ -378,7 +382,7 @@ IRQ:
 
 ; X
 
-@L03:   ldx     PADDL0                  ; get X postion
+        ldx     PADDL0                  ; get X postion
         dex                             ; decrement, since it's 1-based
         stx     XPos
         txa

From 9e155eb096412bc38e3bb71fbcf292743eb7c149 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Tue, 14 Jan 2014 13:59:42 +0100
Subject: [PATCH 21/31] Always print the mouse cursor, even if coordinates
 haven't changed. This makes sure that the cursor is always visible, even if
 the program has written text to the screen (only valid for non-P/M mouse
 callbacks).

---
 libsrc/atari/mou/atrtt.s | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/libsrc/atari/mou/atrtt.s b/libsrc/atari/mou/atrtt.s
index 9d2dac1c9..f58838720 100644
--- a/libsrc/atari/mou/atrtt.s
+++ b/libsrc/atari/mou/atrtt.s
@@ -118,12 +118,8 @@ INSTALL:
         dex
         bpl     @L1
 
-; Be sure the mouse cursor is invisible and at the default location. We
-; need to do that here, because our mouse interrupt handler doesn't set the
-; mouse position if it hasn't changed.
+; Be sure the mouse cursor is invisible and at the default location.
 
-        php
-        sei
         jsr     CHIDE
         lda     XPos
         ldx     XPos+1
@@ -131,7 +127,6 @@ INSTALL:
         lda     YPos
         ldx     YPos+1
         jsr     CMOVEY
-        plp
 
 ; Done, return zero (= MOUSE_ERR_OK)
 
@@ -352,15 +347,18 @@ IRQ:
 
 @L02:   lda     PADDL0
         cmp     #228
-        beq     @Dont
+        beq     @Cont                   ; CF set if equal
         lda     PADDL1
-        cmp     #228
-        bne     @Do
-@Dont:  jmp     @Done
+        cmp     #228                    ; CF set if equal
 
-@Do:    lda     visible
-        beq     @L03
+@Cont:  lda     visible
+        beq     @Go
+        php                             ; remember CF
         jsr     CHIDE
+        plp                             ; restore CF
+
+@Go:    bcc     @L03
+        jmp     @Show
 
 @L03:   ldx     #0
         stx     XPos+1
@@ -484,7 +482,7 @@ IRQ:
         tya
         jsr     CMOVEY
 
-        lda     visible
+@Show:  lda     visible
         beq     @Done
         jsr     CSHOW
 

From 63ce6e28fef215bd571cff9ef35306563d3dea9f Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 18 Jan 2014 00:02:40 +0100
Subject: [PATCH 22/31] Adapt to new mouse driver interface ('prep' and 'draw')

---
 libsrc/atari/mou/atrtt.s | 40 +++++++++++++---------------------------
 1 file changed, 13 insertions(+), 27 deletions(-)

diff --git a/libsrc/atari/mou/atrtt.s b/libsrc/atari/mou/atrtt.s
index f58838720..e373ec015 100644
--- a/libsrc/atari/mou/atrtt.s
+++ b/libsrc/atari/mou/atrtt.s
@@ -49,6 +49,8 @@ HEADER:
 
 CHIDE:  jmp     $0000                   ; Hide the cursor
 CSHOW:  jmp     $0000                   ; Show the cursor
+CPREP:  jmp     $0000                   ; Prepare to move the cursor
+CDRAW:  jmp     $0000                   ; Draw the cursor
 CMOVEX: jmp     $0000                   ; Move the cursor to X coord
 CMOVEY: jmp     $0000                   ; Move the cursor to Y coord
 
@@ -82,9 +84,6 @@ XMax:           .res    2               ; X2 value of bounding box
 YMax:           .res    2               ; Y2 value of bounding box
 Buttons:        .res    1               ; Button mask
 
-
-visible:        .res    1
-
 ; Default values for above variables
 
 .rodata
@@ -118,9 +117,8 @@ INSTALL:
         dex
         bpl     @L1
 
-; Be sure the mouse cursor is invisible and at the default location.
+; Make sure the mouse cursor is at the default location.
 
-        jsr     CHIDE
         lda     XPos
         ldx     XPos+1
         jsr     CMOVEX
@@ -147,8 +145,7 @@ UNINSTALL       = HIDE                  ; Hide cursor on exit
 ; no special action is required besides hiding the mouse cursor.
 ; No return code required.
 
-HIDE:   dec     visible
-        php
+HIDE:   php
         sei
         jsr     CHIDE
         plp
@@ -161,8 +158,7 @@ HIDE:   dec     visible
 ; no special action is required besides enabling the mouse cursor.
 ; No return code required.
 
-SHOW:   inc     visible
-        php
+SHOW:   php
         sei
         jsr     CSHOW
         plp
@@ -221,14 +217,12 @@ MOVE:   php
         sei                             ; No interrupts
 
         pha
-        lda     visible
-        beq     @nohide
         txa
         pha
-        jsr     CHIDE
+        jsr     CPREP
         pla
         tax
-@nohide:pla
+        pla
 
         sta     YPos
         stx     YPos+1                  ; New Y position
@@ -241,15 +235,11 @@ MOVE:   php
         dey
         lda     (sp),y
         sta     XPos                    ; New X position
-
         jsr     CMOVEX                  ; Move the cursor
 
-        lda     visible
-        beq     @Ret
-        
         jsr     CSHOW
 
-@Ret:   plp                             ; Restore interrupt flag
+        plp                             ; Restore interrupt flag
         rts
 
 ;----------------------------------------------------------------------------
@@ -351,13 +341,11 @@ IRQ:
         lda     PADDL1
         cmp     #228                    ; CF set if equal
 
-@Cont:  lda     visible
-        beq     @Go
-        php                             ; remember CF
-        jsr     CHIDE
+@Cont:  php                             ; remember CF
+        jsr     CPREP
         plp                             ; restore CF
 
-@Go:    bcc     @L03
+        bcc     @L03
         jmp     @Show
 
 @L03:   ldx     #0
@@ -482,10 +470,8 @@ IRQ:
         tya
         jsr     CMOVEY
 
-@Show:  lda     visible
-        beq     @Done
-        jsr     CSHOW
+@Show:  jsr     CDRAW
 
-@Done:  clc                             ; Interrupt not "handled"
+        clc                             ; Interrupt not "handled"
         rts
 

From 478da8e51f1e294649b4ee2b861ff15e965b18af Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Thu, 20 Feb 2014 00:45:48 +0100
Subject: [PATCH 23/31] load CASHDR segment into RAM

---
 cfg/atari-cassette.cfg | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/cfg/atari-cassette.cfg b/cfg/atari-cassette.cfg
index ba640a54e..e69abfb4e 100644
--- a/cfg/atari-cassette.cfg
+++ b/cfg/atari-cassette.cfg
@@ -14,7 +14,7 @@ MEMORY {
 #    HEADER:        file = "",               start = $0000, size = $0002;
 
 #     CASHDR: file = %O, start = $3FD, size = 128, fill = yes;
-     CASHDR: file = %O, start = $0, size = 6;
+#     CASHDR: file = %O, start = $0, size = 6;
 # "main program" load chunk
 #    MAINHDR:       file = "",               start = $0000, size = $0004;
     RAM:           file = %O, define = yes, start = %S,    size = $BC20 - __STACKSIZE__ - __RESERVED_MEMORY__ - %S;
@@ -23,7 +23,8 @@ MEMORY {
 SEGMENTS {
 #    EXEHDR:    load = HEADER,     type = ro;
 #    MAINHDR:   load = MAINHDR,    type = ro;
-     CASHDR:   load = CASHDR, type = ro;
+#     CASHDR:   load = CASHDR, type = ro;
+     CASHDR:   load = RAM, type = ro;
      CASINIT:  load = RAM, type = ro;
     STARTUP:   load = RAM,        type = ro,  define = yes;
     LOWCODE:   load = RAM,        type = ro,  define = yes, optional = yes;

From 8178c71de1da66a8093a7a308541a5a1b75441fc Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Thu, 20 Feb 2014 00:56:22 +0100
Subject: [PATCH 24/31] add utility program to write files to cassette

---
 util/atari/w2cas.c | 186 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 186 insertions(+)
 create mode 100644 util/atari/w2cas.c

diff --git a/util/atari/w2cas.c b/util/atari/w2cas.c
new file mode 100644
index 000000000..38c2f0fdf
--- /dev/null
+++ b/util/atari/w2cas.c
@@ -0,0 +1,186 @@
+/* w2cas.c -- write file to cassette
+ *
+ * This program writes a boot file (typically linked with
+ * 'atari-cassette.cfg') to the cassette.
+ * Only files < 32K are supported, since the loading of
+ * larger files requires a special loader inside the program.
+ *
+ * Christian Groessler, chris@groessler.org, 2014
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <6502.h>
+#include <atari.h>
+#include <conio.h>
+
+static int verbose = 1;
+static char C_dev[] = "C:";
+
+static struct __iocb *findfreeiocb(void)
+{
+    struct __iocb *iocb = &IOCB;  /* first IOCB (#0) */
+    int i;
+
+    for (i = 0; i < 8; i++) {
+        if (iocb->handler == 0xff)
+            return iocb;
+        iocb++;
+    }
+    return NULL;
+}
+
+int main(int argc, char **argv)
+{
+    char *filename, *x;
+    char buf[20];
+    FILE *file;
+    unsigned char *buffer;
+    size_t filen, buflen = 32768l + 1;
+    struct regs regs;
+    struct __iocb *iocb = findfreeiocb();
+    int iocb_num;
+
+    if (! iocb) {
+        fprintf(stderr, "couldn't find a free iocb\n");
+        if (_dos_type != 1)
+            cgetc();
+        return 1;
+    }
+    iocb_num = (iocb - &IOCB) * 16;
+    if (verbose)
+        printf("using iocb index $%02X ($%04X)\n", iocb_num, iocb);
+
+    if (argc < 2) {
+        printf("\nfilename: ");
+        x = fgets(buf, 19, stdin);
+        printf("\n");
+        if (! x)
+            return 1;
+        if (*x && *(x + strlen(x) - 1) == '\n')
+            *(x + strlen(x) - 1) = 0;
+        filename = x;
+    }
+    else {
+        filename = *(argv+1);
+    }
+
+    /* allocate buffer */
+    buffer = malloc(buflen);
+    if (! buffer) {
+        buflen = _heapmaxavail() - 8; /* get as much as we can */
+        buffer = malloc(buflen);
+        if (! buffer) {
+            fprintf(stderr, "cannot alloc %ld bytes -- aborting...\n", (long)buflen);
+            if (_dos_type != 1)
+                cgetc();
+            return 1;
+        }
+    }
+    if (verbose)
+        printf("buffer size: %ld bytes\n", (long)buflen);
+
+    /* open file */
+    file = fopen(filename, "rb");
+    if (! file) {
+        free(buffer);
+        fprintf(stderr, "cannot open '%s': %s\n", filename, strerror(errno));
+        if (_dos_type != 1)
+            cgetc();
+        return 1;
+    }
+
+    /* read file -- file length must be < 32K */
+    if (verbose)
+        printf("reading input file...\n");
+    filen = fread(buffer, 1, buflen, file);
+    if (! filen) {
+        fprintf(stderr, "read error\n");
+    file_err:
+        fclose(file);
+        free(buffer);
+        if (_dos_type != 1)
+            cgetc();
+        return 1;
+    }
+    if (filen > 32767l) {
+        fprintf(stderr, "file is too large (must be < 32768)\n");
+        goto file_err;
+    }
+    if (filen == buflen) { /* we have a buffer < 32768 and the file fits into it (and is most probably larger) */
+        fprintf(stderr, "not enough memory\n");
+        goto file_err;
+    }
+    if (verbose)
+      printf("file size: %ld bytes\n", (long)filen);
+
+    /* close input file */
+    fclose(file);
+
+    /* open cassette */
+    if (verbose)
+        printf("opening cassette...\n");
+    iocb->buffer = C_dev;
+    iocb->aux1 = 8;    /* open for output */
+    iocb->aux2 = 128;  /* short breaks and no stop between data blocks */
+    iocb->command = IOCB_OPEN;
+    regs.x = iocb_num;
+    regs.pc = 0xe456;   /* CIOV */
+
+    _sys(&regs);
+    if (regs.y != 1) {
+        fprintf(stderr, "CIO call to open cassette returned %d\n", regs.y);
+        free(buffer);
+        if (_dos_type != 1)
+            cgetc();
+        return 1;
+    }
+
+    /* write file */
+    if (verbose)
+        printf("writing to cassette...\n");
+    iocb->buffer = buffer;
+    iocb->buflen = filen;
+    iocb->command = IOCB_PUTCHR;
+    regs.x = iocb_num;
+    regs.pc = 0xe456;   /* CIOV */
+
+    _sys(&regs);
+    if (regs.y != 1) {
+        fprintf(stderr, "CIO call to write file returned %d\n", regs.y);
+        free(buffer);
+
+        iocb->command = IOCB_CLOSE;
+        regs.x = iocb_num;
+        regs.pc = 0xe456;   /* CIOV */
+        _sys(&regs);
+
+        if (_dos_type != 1)
+            cgetc();
+        return 1;
+    }
+
+    /* free buffer */
+    free(buffer);
+
+    /* close cassette */
+    iocb->command = IOCB_CLOSE;
+    regs.x = iocb_num;
+    regs.pc = 0xe456;   /* CIOV */
+    _sys(&regs);
+
+    if (regs.y != 1) {
+        fprintf(stderr, "CIO call to close cassette returned %d\n", regs.y);
+        if (_dos_type != 1)
+            cgetc();
+        return 1;
+    }
+
+    /* all is fine */
+    printf("success\n");
+    if (_dos_type != 1)
+        cgetc();
+    return 0;
+}

From 09f7eb72c247d59d5f1a0bee80039f118e42c658 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Fri, 21 Feb 2014 22:42:09 +0100
Subject: [PATCH 25/31] remove workaround for _heapmaxavail

---
 util/atari/w2cas.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/util/atari/w2cas.c b/util/atari/w2cas.c
index 38c2f0fdf..050218cfe 100644
--- a/util/atari/w2cas.c
+++ b/util/atari/w2cas.c
@@ -70,7 +70,7 @@ int main(int argc, char **argv)
     /* allocate buffer */
     buffer = malloc(buflen);
     if (! buffer) {
-        buflen = _heapmaxavail() - 8; /* get as much as we can */
+        buflen = _heapmaxavail(); /* get as much as we can */
         buffer = malloc(buflen);
         if (! buffer) {
             fprintf(stderr, "cannot alloc %ld bytes -- aborting...\n", (long)buflen);

From 8b876b2ce50e1c5cb85e62280e9030b9597451fe Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Fri, 21 Feb 2014 22:42:54 +0100
Subject: [PATCH 26/31] document 'atari-cassette.cfg'

---
 doc/atari.sgml | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/doc/atari.sgml b/doc/atari.sgml
index 9f7a3fbc4..908763a3f 100644
--- a/doc/atari.sgml
+++ b/doc/atari.sgml
@@ -221,6 +221,16 @@ that the cartridge doesn't prevent the booting of DOS.
 The option byte will be located at address &dollar;BFFD. For more information
 about its use, see e.g. "Mapping the Atari".
 
+<sect2><tt/atari-cassette.cfg/<p>
+
+This config file can be used to create cassette boot files. It's suited both
+for C and assembly language programs.
+
+The size of a cassette boot file is restricted to 32K. Larger programs
+would need to be split in more parts and the parts to be loaded manually.
+
+To write the generated file to a cassette, an utility program to run
+on an Atari is provided (<tt/util&sol;atari&sol;w2cas.c/).
 
 <sect1><tt/atarixl/ config files<p>
 

From 81fe7a38051030924a55ff278469a70c7e5b43c0 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Fri, 21 Feb 2014 22:43:44 +0100
Subject: [PATCH 27/31] cleanups; split 'header' and 'init' part into two
 source files

---
 cfg/atari-cassette.cfg | 42 ++++++++++++----------------------
 libsrc/atari/cashdr.s  | 51 ++++++++++++++++++++----------------------
 libsrc/atari/casinit.s | 31 +++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 55 deletions(-)
 create mode 100644 libsrc/atari/casinit.s

diff --git a/cfg/atari-cassette.cfg b/cfg/atari-cassette.cfg
index e69abfb4e..1099bf91c 100644
--- a/cfg/atari-cassette.cfg
+++ b/cfg/atari-cassette.cfg
@@ -1,41 +1,27 @@
 FEATURES {
-    STARTADDRESS: default = $1000;
+    STARTADDRESS: default = $0700;
 }
 SYMBOLS {
-_cas_init: type = import;
-    __STACKSIZE__:       type = weak, value = $0800; # 2k stack
+    __STACKSIZE__:       type = weak,   value = $0800; # 2k stack
+    __RESERVED_MEMORY__: type = weak,   value = $0000;
     __STARTADDRESS__:    type = export, value = %S;
-    __RESERVED_MEMORY__: type = weak, value = $0000;
+    _cas_hdr:            type = import;
 }
 MEMORY {
     ZP:            file = "", define = yes, start = $0082, size = $007E;
-
-# file header, just $FFFF
-#    HEADER:        file = "",               start = $0000, size = $0002;
-
-#     CASHDR: file = %O, start = $3FD, size = 128, fill = yes;
-#     CASHDR: file = %O, start = $0, size = 6;
-# "main program" load chunk
-#    MAINHDR:       file = "",               start = $0000, size = $0004;
     RAM:           file = %O, define = yes, start = %S,    size = $BC20 - __STACKSIZE__ - __RESERVED_MEMORY__ - %S;
-#    TRAILER:       file = "",               start = $0000, size = $0006;
 }
 SEGMENTS {
-#    EXEHDR:    load = HEADER,     type = ro;
-#    MAINHDR:   load = MAINHDR,    type = ro;
-#     CASHDR:   load = CASHDR, type = ro;
-     CASHDR:   load = RAM, type = ro;
-     CASINIT:  load = RAM, type = ro;
-    STARTUP:   load = RAM,        type = ro,  define = yes;
-    LOWCODE:   load = RAM,        type = ro,  define = yes, optional = yes;
-    INIT:      load = RAM,        type = ro,                optional = yes;
-    CODE:      load = RAM,        type = ro,  define = yes;
-    RODATA:    load = RAM,        type = ro;
-    DATA:      load = RAM,        type = rw;
-    BSS:       load = RAM,        type = bss, define = yes;
-    ZEROPAGE:  load = ZP,         type = zp;
-    EXTZP:     load = ZP,         type = zp,                optional = yes;
-#    AUTOSTRT:  load = TRAILER,    type = ro;
+    CASHDR:    load = RAM, type = ro;
+    STARTUP:   load = RAM, type = ro,  define = yes, optional = yes;
+    LOWCODE:   load = RAM, type = ro,  define = yes, optional = yes;
+    INIT:      load = RAM, type = ro,                optional = yes;
+    CODE:      load = RAM, type = ro,  define = yes;
+    RODATA:    load = RAM, type = ro,                optional = yes;
+    DATA:      load = RAM, type = rw,                optional = yes;
+    BSS:       load = RAM, type = bss, define = yes, optional = yes;
+    ZEROPAGE:  load = ZP,  type = zp,                optional = yes;
+    EXTZP:     load = ZP,  type = zp,                optional = yes;
 }
 FEATURES {
     CONDES: type    = constructor,
diff --git a/libsrc/atari/cashdr.s b/libsrc/atari/cashdr.s
index 8d3d0868a..99aefe68f 100644
--- a/libsrc/atari/cashdr.s
+++ b/libsrc/atari/cashdr.s
@@ -1,40 +1,37 @@
 ;
 ; Cassette boot file header
 ;
-; Christian Groessler, chris@groessler.org, 2013
+; Christian Groessler, chris@groessler.org, 2014
 ;
 
+;DEBUG   = 1
+
 .ifndef __ATARIXL__
 
-	.include "atari.inc"
+        .include "atari.inc"
 
-	.import	__BSS_RUN__, __STARTADDRESS__, start
-	.export	_cas_init
+        .import __BSS_RUN__, __STARTADDRESS__, _cas_init
+        .export _cas_hdr
+
+.assert ((__BSS_RUN__ - __STARTADDRESS__ + 127) / 128) < $101, error, "File to big to load from cassette"
+
+
+; for a description of the cassette header, see De Re Atari, appendix C
 
 .segment        "CASHDR"
 
-	.byte	0	; ignored
-	.byte	<((__BSS_RUN__ - __STARTADDRESS__ + 127 + 6) / 128)
-	.word	__STARTADDRESS__
-	.word	_cas_init
+_cas_hdr:
+        .byte   0                       ; ignored
+        .byte   <((__BSS_RUN__ - __STARTADDRESS__ + 127) / 128)         ; # of 128-byte records to read
+        .word   __STARTADDRESS__        ; load address
+        .word   _cas_init               ; init address
 
-.segment	"CASINIT"
+.ifdef  DEBUG
+        lda     #33
+        ldy     #80
+        sta     (SAVMSC),y
+.endif
+        clc
+        rts
 
-	lda	#33
-	ldy	#80
-	sta	(SAVMSC),y
-	clc
-	rts
-
-_cas_init:
-	lda	#34
-	ldy	#81
-	sta	(SAVMSC),y
-
-	lda	#<start
-	sta	DOSVEC
-	lda	#>start
-	sta	DOSVEC+1
-	rts
-
-.endif	; .ifdef __ATARIXL__
+.endif  ; .ifdef __ATARIXL__
diff --git a/libsrc/atari/casinit.s b/libsrc/atari/casinit.s
new file mode 100644
index 000000000..c91989aad
--- /dev/null
+++ b/libsrc/atari/casinit.s
@@ -0,0 +1,31 @@
+;
+; Cassette boot file init routine
+;
+; Christian Groessler, chris@groessler.org, 2014
+;
+
+;DEBUG   = 1
+
+.ifndef __ATARIXL__
+
+        .include "atari.inc"
+
+        .import start
+        .export _cas_init
+
+.segment        "INIT"
+
+_cas_init:
+.ifdef  DEBUG
+        lda     #34
+        ldy     #81
+        sta     (SAVMSC),y
+.endif
+
+        lda     #<start
+        sta     DOSVEC
+        lda     #>start
+        sta     DOSVEC+1
+        rts
+
+.endif  ; .ifdef __ATARIXL__

From 4b58a20b8c414527af035ec37c32fc38621d8c2b Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Fri, 21 Feb 2014 23:34:05 +0100
Subject: [PATCH 28/31] Increase the default start address a bit in case BASIC
 is started ('OPTION' not pressed at power on). It would otherwise overwrite
 parts at the beginning.

---
 cfg/atari-cassette.cfg | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cfg/atari-cassette.cfg b/cfg/atari-cassette.cfg
index 1099bf91c..2116aecd0 100644
--- a/cfg/atari-cassette.cfg
+++ b/cfg/atari-cassette.cfg
@@ -1,5 +1,5 @@
 FEATURES {
-    STARTADDRESS: default = $0700;
+    STARTADDRESS: default = $0900;
 }
 SYMBOLS {
     __STACKSIZE__:       type = weak,   value = $0800; # 2k stack

From dec2376195db1faf89aba3dba9c30373768f320d Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 1 Mar 2014 01:48:45 +0100
Subject: [PATCH 29/31] small wording change

---
 doc/atari.sgml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/atari.sgml b/doc/atari.sgml
index 908763a3f..781fdf03b 100644
--- a/doc/atari.sgml
+++ b/doc/atari.sgml
@@ -229,7 +229,7 @@ for C and assembly language programs.
 The size of a cassette boot file is restricted to 32K. Larger programs
 would need to be split in more parts and the parts to be loaded manually.
 
-To write the generated file to a cassette, an utility program to run
+To write the generated file to a cassette, a utility to run
 on an Atari is provided (<tt/util&sol;atari&sol;w2cas.c/).
 
 <sect1><tt/atarixl/ config files<p>

From 69f81f6d67a2fb3127adbca68f508c65c1d4dd33 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Sat, 1 Mar 2014 13:10:01 +0100
Subject: [PATCH 30/31] make 'w2cas' a targetutil

---
 doc/atari.sgml                                  |  2 +-
 libsrc/Makefile                                 |  1 +
 libsrc/atari/targetutil/Makefile.inc            | 14 ++++++++++++++
 {util/atari => libsrc/atari/targetutil}/w2cas.c |  0
 4 files changed, 16 insertions(+), 1 deletion(-)
 create mode 100644 libsrc/atari/targetutil/Makefile.inc
 rename {util/atari => libsrc/atari/targetutil}/w2cas.c (100%)

diff --git a/doc/atari.sgml b/doc/atari.sgml
index 781fdf03b..867f25c1d 100644
--- a/doc/atari.sgml
+++ b/doc/atari.sgml
@@ -230,7 +230,7 @@ The size of a cassette boot file is restricted to 32K. Larger programs
 would need to be split in more parts and the parts to be loaded manually.
 
 To write the generated file to a cassette, a utility to run
-on an Atari is provided (<tt/util&sol;atari&sol;w2cas.c/).
+on an Atari is provided (<tt/W2CAS.COM/).
 
 <sect1><tt/atarixl/ config files<p>
 
diff --git a/libsrc/Makefile b/libsrc/Makefile
index 3d8277ae5..89f952376 100644
--- a/libsrc/Makefile
+++ b/libsrc/Makefile
@@ -102,6 +102,7 @@ MKINC = $(GEOS) \
         nes
 
 TARGETUTIL = apple2 \
+             atari \
              geos-apple
 
 GEOSDIRS = common      \
diff --git a/libsrc/atari/targetutil/Makefile.inc b/libsrc/atari/targetutil/Makefile.inc
new file mode 100644
index 000000000..eb371e111
--- /dev/null
+++ b/libsrc/atari/targetutil/Makefile.inc
@@ -0,0 +1,14 @@
+ifeq ($(TARGET),atari)
+
+DEPS += ../wrk/$(TARGET)/w2cas.d
+
+../wrk/$(TARGET)/w2cas.o: CC65FLAGS:=-O -W error
+../wrk/$(TARGET)/w2cas.o: $(SRCDIR)/targetutil/w2cas.c | ../wrk/$(TARGET)
+	$(COMPILE_recipe)
+
+../targetutil/W2CAS.COM: ../wrk/$(TARGET)/w2cas.o ../lib/$(TARGET).lib | ../targetutil
+	$(LD65) -o $@ -t $(TARGET) $^
+
+$(TARGET): ../targetutil/W2CAS.COM
+
+endif
diff --git a/util/atari/w2cas.c b/libsrc/atari/targetutil/w2cas.c
similarity index 100%
rename from util/atari/w2cas.c
rename to libsrc/atari/targetutil/w2cas.c

From 7a9fa9d4cd69bebf10054f123c836cde768e6884 Mon Sep 17 00:00:00 2001
From: Christian Groessler <chris@groessler.org>
Date: Mon, 3 Mar 2014 18:12:11 +0100
Subject: [PATCH 31/31] rename W2CAS.COM to w2cas.com (lowercase)

---
 doc/atari.sgml                       | 2 +-
 libsrc/atari/targetutil/Makefile.inc | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/doc/atari.sgml b/doc/atari.sgml
index 867f25c1d..79a3e3015 100644
--- a/doc/atari.sgml
+++ b/doc/atari.sgml
@@ -230,7 +230,7 @@ The size of a cassette boot file is restricted to 32K. Larger programs
 would need to be split in more parts and the parts to be loaded manually.
 
 To write the generated file to a cassette, a utility to run
-on an Atari is provided (<tt/W2CAS.COM/).
+on an Atari is provided in the <tt/targetutil/ directory (<tt/w2cas.com/).
 
 <sect1><tt/atarixl/ config files<p>
 
diff --git a/libsrc/atari/targetutil/Makefile.inc b/libsrc/atari/targetutil/Makefile.inc
index eb371e111..a54d96100 100644
--- a/libsrc/atari/targetutil/Makefile.inc
+++ b/libsrc/atari/targetutil/Makefile.inc
@@ -6,9 +6,9 @@ DEPS += ../wrk/$(TARGET)/w2cas.d
 ../wrk/$(TARGET)/w2cas.o: $(SRCDIR)/targetutil/w2cas.c | ../wrk/$(TARGET)
 	$(COMPILE_recipe)
 
-../targetutil/W2CAS.COM: ../wrk/$(TARGET)/w2cas.o ../lib/$(TARGET).lib | ../targetutil
+../targetutil/w2cas.com: ../wrk/$(TARGET)/w2cas.o ../lib/$(TARGET).lib | ../targetutil
 	$(LD65) -o $@ -t $(TARGET) $^
 
-$(TARGET): ../targetutil/W2CAS.COM
+$(TARGET): ../targetutil/w2cas.com
 
 endif