From: Greg King Date: Mon, 9 Sep 2013 21:03:40 +0000 (-0400) Subject: Added a cbm510 lightpen driver. X-Git-Tag: V2.15~230^2~2 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=e0c8c7dcb15551df2fd0d17ff2d4097e6b2dbc68;p=cc65 Added a cbm510 lightpen driver. It's similar to the current c64 and c128 lightpen drivers. --- diff --git a/asminc/cbm510.inc b/asminc/cbm510.inc index dfeee1d4e..abc363728 100644 --- a/asminc/cbm510.inc +++ b/asminc/cbm510.inc @@ -23,6 +23,9 @@ YSIZE = 25 ;----------------------------------------------------------------------------- ; I/O Definitions +; Note: These numbers aren't addresses. They are offsets from the start of +; each chip's register set. They are used in the indirect indexed addressing +; mode. ; I/O $d800: VIC-II @@ -66,6 +69,9 @@ VIC_CTRL2 = $16 VIC_HLINE = $12 +VIC_LPEN_X = $13 +VIC_LPEN_Y = $14 + VIC_VIDEO_ADR = $18 VIC_IRR = $19 ; Interrupt request register diff --git a/include/cbm510.h b/include/cbm510.h index 797b2c4fa..b6c8892b1 100644 --- a/include/cbm510.h +++ b/include/cbm510.h @@ -118,7 +118,8 @@ /* The addresses of the static drivers */ -extern void cbm510_joy_mou[]; /* Referred to by mouse_static_stddrv[] */ +extern void cbm510_170c_mou[]; +extern void cbm510_joy_mou[]; /* Referred to by mouse_static_stddrv[] */ extern void cbm510_ram_emd[]; extern void cbm510_std_joy[]; /* Referred to by joy_static_stddrv[] */ extern void cbm510_std_ser[]; diff --git a/libsrc/cbm510/mou/cbm510-170c.s b/libsrc/cbm510/mou/cbm510-170c.s new file mode 100644 index 000000000..bf10cd24b --- /dev/null +++ b/libsrc/cbm510/mou/cbm510-170c.s @@ -0,0 +1,442 @@ +; +; Driver for the Inkwell Systems 170-C lightpen. +; +; 2013-09-05, Greg King +; + + .include "zeropage.inc" + .include "../extzp.inc" + + .include "mouse-kernel.inc" + .include "cbm510.inc" + + .macpack generic + +; ------------------------------------------------------------------------ +; Header. Includes jump table. + +.segment "JUMPTABLE" + +HEADER: + +; Driver signature + + .byte $6d, $6f, $75 ; ASCII "mou" + .byte MOUSE_API_VERSION ; Mouse driver API version number + +; Library reference + +LIBREF: .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_EARLY_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 co-ord. +CMOVEY: jmp $0000 ; Move the cursor to Y co-ord. + + +;---------------------------------------------------------------------------- +; Constants + +SCREEN_WIDTH = XSIZE * 8 +SCREEN_HEIGHT = YSIZE * 8 + +;---------------------------------------------------------------------------- +; 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 +; re-order them. + +.rodata + +; Default values for below variables +; (We use ".proc" because we want to define both a label and a scope.) + +.proc DefVars + .word 0 ; XMin + .word 0 ; YMin + .word SCREEN_WIDTH - 1 ; XMax + .word SCREEN_HEIGHT - 1 ; YMax +.endproc + +.bss + +Vars: +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 + +XPos: .res 2 ; Current lightpen position, X +YPos: .res 2 ; Current lightpen position, Y + +OldPenX: .res 1 ; Previous HW-counter values +OldPenY: .res 1 + +.data + +; Default Inkwell calibration. +; The first number is the width of the left border; +; the second number is the actual calibration value. + +XOffset: .byte (24 + 24) / 2 ; x-offset + +; Jump to a function that puts a new calibration value into XOffset. +Calibrate: jmp $0000 + + +.code + +;---------------------------------------------------------------------------- +; INSTALL routine. Is called after the driver is loaded into memory. If +; possible, check if the hardware is present. +; Must return a MOUSE_ERR_xx code in .XA. + +INSTALL: + +; Initiate variables. Just copy the default stuff over. + + ldx #.sizeof (DefVars) - 1 +@L0: lda DefVars,x + sta Vars,x + dex + bpl @L0 + + ldx #15 ; Change to system bank + stx IndReg + ldy #VIC_LPEN_X + lda (vic),y + sta OldPenX + ldy #VIC_LPEN_Y + lda (vic),y + sta OldPenY + ldx ExecReg ; Change back to execution bank + stx IndReg + +; Call a calibration function through the library-reference. + + lda LIBREF + ldx LIBREF+1 + sta ptr1 ; Point to mouse_adjuster + stx ptr1+1 + ldy #1 + lda (ptr1),y + bze @L1 ; Don't call pointer if it's NULL + sta Calibrate+2 ; Point to function + dey + lda (ptr1),y + sta Calibrate+1 + lda #XOffset + jsr Calibrate + +; Be sure that the lightpen cursor is invisible and at the default location. +; It needs to be done here because the lightpen interrupt handler doesn't +; set the lightpen position if it hasn't changed. + +@L1: sei + jsr CHIDE + + lda #<(SCREEN_HEIGHT / 2) + ldx #>(SCREEN_HEIGHT / 2) + jsr MoveY + lda #<(SCREEN_WIDTH / 2) + ldx #>(SCREEN_WIDTH / 2) + jsr MoveX + cli + +; Done, return zero. + + lda #MOUSE_ERR_OK + tax + 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 lightpen pointer. The mouse kernel manages +; a counter for calls to show/hide, and the driver entry point is called only +; if the mouse is currently visible, and should get hidden. For most drivers, +; no special action is required besides hiding the lightpen cursor. +; No return code required. + +HIDE: sei + jsr CHIDE + cli + rts + +;---------------------------------------------------------------------------- +; SHOW routine. Is called to show the lightpen pointer. The mouse kernel manages +; a counter for calls to show/hide, and the driver entry point is called only +; if the mouse is currently hidden, and should become visible. For most drivers, +; no special action is required besides enabling the lightpen cursor. +; No return code required. + +SHOW: sei + jsr CSHOW + cli + rts + +;---------------------------------------------------------------------------- +; SETBOX: Set the lightpen bounding box. The parameters are passed as they come +; from the C program, that is, a pointer to a mouse_box struct in .XA. +; No checks are done if the lightpen is currently inside the box, that 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 lightpen bounding box. The parameters are passed as they +; come from the C program, that is, a pointer to a mouse_box struct in .XA. + +GETBOX: sta ptr1 + stx ptr1+1 ; Save data pointer + + ldy #.sizeof (MOUSE_BOX) - 1 +@L1: lda XMin,y + sta (ptr1),y + dey + bpl @L1 + 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 .XA. 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 + jsr MoveY + + ldy #$01 + lda (sp),y + tax + dey + lda (sp),y + jsr MoveX ; Move the cursor + + cli ; Allow interrupts + rts + +;---------------------------------------------------------------------------- +; BUTTONS: Return the button mask in .XA. + +BUTTONS: + ldx #15 ; To system bank + stx IndReg + ldy #CIA::PRB + lda (cia2),y ; Read joystick inputs + ldx ExecReg ; Back to execution bank + stx IndReg + +; Joystick 1, directions in bits 3-0. +; Make the lightpen button look like a 1351 mouse. + + asl a ; Move joystick-left bit ... + asl a ; ... to fire-button bit + eor #MOUSE_BTN_LEFT + and #MOUSE_BTN_LEFT + ldx #>0 + rts + +;---------------------------------------------------------------------------- +; POS: Return the lightpen 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 + +;---------------------------------------------------------------------------- +; INFO: Returns lightpen 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'll just +; call _mouse_pos to initiate the struct pointer, and fill the position +; fields. + +INFO: jsr POS + +; Fill in the button state + + jsr BUTTONS ; Will not touch ptr1 + 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 .XA. +; + +IOCTL: lda #MOUSE_ERR_INV_IOCTL + rts + +;---------------------------------------------------------------------------- +; IRQ: Irq.-handler entry point. Called as a subroutine, but in the 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: ldx #15 ; To system bank + stx IndReg + +; Read the VIC-II lightpen registers. + + ldy #VIC_LPEN_Y + lda (vic),y + cmp OldPenY + +; Skip processing if nothing has changed. + + beq @SkipY + sta OldPenY + ldx ExecReg ; Back to execution bank + stx IndReg + +; Subtract the height of the top border, so that the lightpen co-ordinate +; will match the TGI co-ordinate. + + sub #50 + tay ; Remember low byte + ldx #>0 + +; Limit the Y co-ordinate to the bounding box. + + txa + 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: tya + jsr MoveY + + ldx #15 ; To system bank + stx IndReg +@SkipY: ldy #VIC_LPEN_X + lda (vic),y + ldx ExecReg ; Back to execution bank + stx IndReg + cmp OldPenX + +; Skip processing if nothing has changed. + + beq @SkipX + sta OldPenX + +; Adjust the value by the calibration offset. + + sub XOffset + +; Calculate the new X co-ordinate. +; The VIC-II register is eight bits; but, the screen co-ordinate is nine bits. +; Therefore, the VIC-II number is doubled. Then, it points to every other pixel; +; but, it can reach across the screen. + + asl a + tay ; Remember low byte + lda #>0 + rol a + tax ; Remember high byte + +; Limit the X co-ordinate 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: tya + jsr MoveX + +; Done + +@SkipX: clc ; Interrupt not "handled" + rts + +; Move the lightpen pointer to the new Y pos. + +MoveY: sta YPos + stx YPos+1 + jmp CMOVEY + +; Move the lightpen pointer to the new X pos. + +MoveX: sta XPos + stx XPos+1 + jmp CMOVEX diff --git a/libsrc/cbm510/pencalib.c b/libsrc/cbm510/pencalib.c new file mode 100755 index 000000000..3ff3faf4c --- /dev/null +++ b/libsrc/cbm510/pencalib.c @@ -0,0 +1,94 @@ +/* +** Calibrate lightpen drivers to the current video hardware. +** +** 2013-09-05, Greg King +** +*/ + + +#include +#include +#include + + +#define COMMAND1 "Adjust by clicking on line." +#define COMMAND2 "Finish by clicking off bar." + + +/* +** There is a delay between when the VIC sends its signal, and when the display +** shows that signal. There is another delay between the display and when +** the lightpen says that it saw that signal. Each display and pen is different. +** Therefore, the driver must be calibrated to them. A white bar is painted on +** the screen; and, a line is drawn down the middle of it. When the user clicks +** on that line, the difference between its position and where the VIC thinks +** that the pen is pointing becomes an offset that is subtracted from what the +** VIC sees. +*/ +void __fastcall__ pen_calibrate (unsigned char *XOffset) +{ + unsigned char oldBg = bgcolor (COLOR_BLUE); + unsigned char oldText = textcolor (COLOR_GRAY3); + unsigned char oldRev = revers (1); + unsigned char sprite0Color = peekbsys ((unsigned)&VIC.spr_color[0]); + unsigned char width, width2, height, height4, height8; + struct mouse_info info; + + screensize (&width, &height); + width2 = width / 2; + height4 = height / 4; + height8 = height4 * 8; + + /* Draw a bar and line. */ + + clrscr (); + cclearxy (0, height4, height4 * width); + cvlinexy (width2, height4 + 1, height4 - 2); + revers (0); + + /* Print instructions. */ + + cputsxy (width2 - (sizeof COMMAND1) / 2, height / 2 + 1, COMMAND1); + cputsxy (width2 - (sizeof COMMAND2) / 2, height / 2 + 3, COMMAND2); + + pokebsys ((unsigned)&VIC.spr_color[0], COLOR_GRAY2); + mouse_show (); + mouse_move (width2 * 8, height8 / 2); + + for (;;) { + /* Wait for the main button to be released. */ + + do ; while ((mouse_buttons () & MOUSE_BTN_LEFT)); + + /* Wait for the main button to be pressed. */ + + do { + mouse_info (&info); + } while (!(info.buttons & MOUSE_BTN_LEFT)); + + /* Find out if the pen is on or off the bar. */ + + if (info.pos.y < height8 || info.pos.y >= height8 * 2) { + break; + } + + /* On the bar; adjust the offset. */ + /* Characters are eight pixels wide. + ** The VIC-II sees every other pixel; + ** so, we use half of the difference. + */ + + *XOffset += (info.pos.x - (width2 * 8 + 8/2)) / 2; + } + + /* Off the bar; wait for the main button to be released. */ + + do ; while ((mouse_buttons () & MOUSE_BTN_LEFT)); + + mouse_hide (); + pokebsys ((unsigned)&VIC.spr_color[0], sprite0Color); + revers (oldRev); + textcolor (oldText); + bgcolor (oldBg); + clrscr (); +}