From: dempson@actrix.gen.nz (David Empson) Newsgroups: comp.sys.apple2.programmer Subject: Re: Reading the joystick Date: Wed, 30 Oct 1996 02:59:41 +1300 Organization: Empsoft Message-ID: <19961030025941765980@dempson.actrix.gen.nz> References: <54gvs2$m0s@newsbf02.news.aol.com> <19961024225153181075@dempson.actrix.gen.nz> <54us7b$dsk@insosf1.netins.net> Michael M. Morrison wrote: > These same kinds of speed problems can be seen when dealing with the > joystick on the PC. The way to get around it is to have the user calibrate > their joystick (store values the extremes & center). Once you know the values > at the extremes & center, it is easy to figure out where the joystick is. > As a bonus, it doesn't matter what speed the computer is running, or who > made the joystick (in case the pots are way out of tolerance). Yes, that would work, but it would produce widley varying resolutions, depending on the speed of the machine. Special handling would be needed for accelerators: at the very least, pre-running a loop to ensure it has been cached (the timing glitch is probably small enough to be ignored). A polling loop would then be something like this: read_joystick start ; Return the current joystick X and Y position in X and Y registers. ; The return values are very dependent on the CPU speed. longa on longi on sep #$20 longa off ; 8 bit accumulator ldx #$7000 txy ; initialize counters to near $8000 phb ; Pre-read the loop code to cache it with an accelerator phk plb ; DBR = PBR ldx #trigger preread lda |0,x ; Force ABSOLUTE INDEXED mode inx cpx #done blt preread pea $0000 plb plb ; DBR = $00 phd pea $C000 sei pld ; DPR = $C000 trigger lda $70 ; Trigger the paddle timeouts ; Initial loop: 19 cycles per count of both X and Y. loop inx bmi done bit $64 bmi xdone iny bmi timeout bit $65 bpl loop ; Keep counting X with same number of cycles and I/O access pattern. xloop inx bmi done bit $64 bmi done nop nop bit $65 bra xloop ; Keep counting Y with same number of cycles and I/O access pattern. yloop nop nop bit $64 nop xdone iny bmi done bit $65 bpl yloop done rep #$20 longa on ; Restore 16-bit accumulator pld ; Restore direct page cli plb ; Restore data bank rts end Some improvement is possible, e.g. by only counting using one index register until either of the paddles have timed out. As it stands, this gives 19 CPU cycles per loop. At 1 MHz, this gives a maximum count of about 157 (compared to 255 for the standard 8-bit ROM routine). Timeout will take 19 ms. At 2.6 MHz, each loop would take 17 fast cycles plus 2 slow cycles, plus synchronization delays. Assuming worst case (1 microsecond synchronization time), this is about 10.5 microseconds per loop, which gives a maxmium count of about 285. The best case is 8.5 microseconds per loop, maximum count is 352. Timeout will take 8.7 to 10.7 milliseconds. At 8 MHz: 4.1 to 6.1 microseconds per loop, maximum count is 491 to 731, timeout is 4.2 to 6.2 milliseconds. At 14 MHz: 3.2 to 5.2 microseconds per loop, maximum count is 576 to 937, timeout is 3.3 to 5.3 milliseconds. A slightly longer timeout may be needed to cope with the fastest accelerator (e.g. initialize X to $6C00 instead of $7000, which will give a minimum timeout of 4.1 ms at 14 MHz, 3.6 ms at 20 MHz). If anyone wants to try out this code, feel free: I hereby donate it to the public domain. I haven't tested it myself, so let me know how it works. If you want to call it from a high level language, some additional code will be needed to set up the entry and exit conditions, and to return the results in a suitable form, e.g. a VAR parameter record or pointed-to structure. > A simple counting loop should be all that is required, unless DMA transfers > can mess up software timing loops on the Apple (?). There should be no need > to switch to 1mhz unless the joystick hardware requires it. Interrupts must be locked out for the duration of the timing operation (up to 3 ms for a timeout to occur), which is enough to interfere with AppleTalk communication, and to lose characters on the serial ports. The Apple II doesn't cope well with background DMA: it would destroy a floppy disk if a DMA cycle occurred during a disk write, for example. The IIgs is worse still: it can only handle one active DMA device, because it only has a single DMA bank register; DMA is commonly used by SCSI cards, but only for polled block transfers of data. We can therefore ignore DMA. Switching to 1 MHz is recommended to guarantee timing. If the computer is running in "fast" mode, it is not possible to time code loops precisely, because memory refresh operations steal cycles from the processor. It should be possible to compensate for this over a long enough period, as long as you know where your code is executing from - different speeds apply for code in ROM (2.8 MHz), fast RAM (2.6 MHz with refresh cycles) and slow RAM (1 MHz). The I/O accesses are always at 1 MHz, which introduces synchronization delays between the fast and slow side of the computer, which are difficult to predict. If the system is force to run at 1 MHz, the memory refresh is handled transparently, and the only glitches in timing are the stretched cycles (every 65th cycle is stretch by one cycle of the 7M clock, if I remember right). In addition, all accelerators are designed to slow down to synchronize with the computer while it is running at 1 MHz, whereas they run at full speed (assuming no cache misses, writes or I/O accesses) if the computer is running in fast mode. On the ZIP GS, there is an optional joystick delay, which will cause the above code to run at 2.6 MHz (assuming the IIgs is running in "fast" mode). I'm not sure how the TransWarp GS handles joystick accesses. > You should probably use use a 16 bit value for the counter, too. Yep. -- David Empson dempson@actrix.gen.nz Snail Mail: P.O. Box 27-103, Wellington, New Zealand