16K Cassette or Disk
by Kyle Peacock
Well, this is it. For the past two issues I've been babbling about how much we need assembly language to accomplish smooth fine scrolling. Hopefully you've also been keeping up with Tom Hudson's Boot Camp series. If not, don't sweat. I've included both BASIC and assembly listings so that novices and advanced programmers alike can enjoy the pleasures of fine scrolling. If you've been keeping up and have a basic understanding of the material covered in issues 13 through 15, you are already well on your way to mastering fine scrolling (and maybe even publishing a best-selling game).
The following BASIC listing contains all the code we need to secure our objective. The assemblylanguage routines are contained in the DATA statements on Lines 8000-10000. All you need to do is get a grip on the parameters passed to my USR routine and the restrictions on said parameters.
The USR call in Line 790 will initialize things. This line can be located anywhere you like, as long as all the proper strings have been initialized. I've done my best to be as diverse as possible. Consequently, there are slightly over a dozen parameters to worry about. Let's examine the USR call and its associated parameters:
A=USR(INIT,VBLANK,DLIST,LINES,MINX,MAXX, MINY,MAXY,HMAX,VMAX,HSPEED,VSPEED,STICK, HINVERT,VINVERT,PAGES)
The above MIN and MAX parameters dictate the outer boundaries of the scrolling area. Since smooth-scrolling requires the changing of hardware registers and the updating of LMS operands, the assembly-language routine needs to keep track of the number of operand updates (e.g.; byte shifts). If the specified boundaries are exceeded, scrolling is halted. MINX and MINY are associated with the leftmost and uppermost boundaries, respectively. MAXX and MAXY are associated with the rightmost and lowermost boundaries. MAXY should be initialized to LINES, minus the total number of LMS instructions in your display list.
Graphics Mode | HMAX Value |
---|---|
0 | 3 |
1 | 7 |
2 | 7 |
3 | 15 |
4 | 15 |
5 | 7 |
6 | 7 |
6+ | 7 |
7 | 3 |
7+ | 3 |
8 | 3 |
The SPEED parameters dictate how fast the display will scroll. The value stored in these parameters should be specified in jiffies (60ths of a second). For example, a value of 1 will cause the display to scroll one position every 1/60 of a second. A value of 60 will cause the display to scroll once every second. A value of zero will terminate scrolling altogether. A negative value in these parameters will probably screw things up, so don't try it!
The INVERT parameters invert the scrolling direction when they have a non-zero value. For example, if HINVERT is zero, the display will scroll to the left when the joystick is moved to the left. If HINVERT is one, the display will scroll to the right when the joystick is moved to the left. These parameters are useful when you wish to create a scrolling map-like display.
Although I have tried to incorporate as many features as possible into these routines, some of you out there may find they will not work for your particular application. For example, the routines will bomb out if you have an LMS instruction that is over 255 bytes away from the beginning of the display list. This problem cannot be easily remedied, as it was a design consideration. Should you encounter any other strange problems, feel free to send me a disk or tape with your program, an explanation of the problem, and a self-addressed stamped envelope. Time permitting, I'll asess the damage and (hopefully) come up with a solution.
Wow! Other than the program listing, I'm all done. I had no idea how involving this column would be. From now on I'll stick to playing and reviewing games. Maybe one day I'll review your masterpieces, incorporating fine scrolling. Until then, see you in the funny papers.
100 DATA 526,476,135,536,542,770,544,9
5,988,101,426,664,82,977,88,6950
250 DATA 244,226,971,100,692,844,81,32
1,649,729,188,96,254,794,626,6815
400 DATA 895,83,537,651,890,95,126,944
,705,9,524,871,906,91,167,7494
550 DATA 97,473,854,927,109,517,87,441
,410,96,687,102,929,108,391,6228
700 DATA 275,677,843,103,562,905,727,1
07,7,81,984,717,94,228,100,6410
850 DATA 748,499,339,866,349,748,372,3
20,336,503,504,765,375,771,347,7842
1000 DATA 275,69,277,91,579,580,581,58
2,583,584,578,36,289,995,291,6390
8030 DATA 887,727,417,132,632,360,583,
441,137,645,602,597,879,883,462,8384
8180 DATA 674,461,577,153,472,606,609,
422,417,608,872,762,339,468,561,8001
9000 DATA 291,457,293,620,480,337,650,
474,402,337,885,587,443,344,886,7486
9150 DATA 342,484,688,407,336,434,886,
912,486,653,594,195,472,192,601,7682
9300 DATA 453,616,618,347,586,583,460,
418,369,480,373,492,175,640,390,7000
9450 DATA 625,174,448,594,664,616,330,
670,492,516,784,664,670,923,625,8795
9600 DATA 953,188,554,1695
FSCROLL4.ASM is available in ATASCII format
0100 .OPT NO LIST
0110 ;
0120 ; **************
0130 ; SYSTEM EQUATES
0140 ; **************
0150 ;
0160 POKMSK = $10 ;POKEY INTERRUPT
0170 SDLSTL = $0230 ;DLIST POINTER
0180 STICK0 = $0278 ;JOYSTICK PORT
0190 IRQEN = $D20E ;INTER. REQUEST
0200 HSCROL = $D404 ;SCROLL REGISTER
0210 VSCROL = $D405 ;SCROLL REGISTER
0220 SETVBV = $E45C ;SET TIMERS
0230 XITVBV = $E462 ;EXIT DEF VBLANK
0240 ;
0250 ; **********************
0260 ; MEMORY USAGE & EQUATES
0270 ; **********************
0280 ;
0290 LISTLO = $0600 ;DLIST LO-BYTE
0300 LISTHI = $0601 ;DLIST HI-BYTE
0310 MINX = $0602 ;LEFTMOST LIMIT
0320 MINY = $0603 ;UPPER LIMIT
0330 MAXX = $0604 ;RIGHTMOST LIMIT
0340 MAXY = $0605 ;LOWER LIMIT
0350 HMAX = $0606 ;LARGEST HSCROL
0360 VMAX = $0607 ;LARGEST VSCROL
0370 ALINES = $0608 ;# OF D-LINES
0380 STICK = $0609 ;JOYSTICK #
0390 HSPEED = $060A ;HOR. SPEED
0400 VSPEED = $060B ;VER. SPEED
0410 HINVERT = $060C ;INVERSION FLAG
0420 VINVERT = $060D ;INVERSION FLAG
0430 PAGES = $CB ;SCREEN PAGES
0440 ZPAGE = $CD ;ZERO PAGE INDEX
0450 ;
0460 ; **************************
0470 ; MISCELLANEOUS MEMORY USAGE
0480 ; **************************
0490 ;
0500 HBIT = $060E ;'HSCROL' COPY
0510 VBIT = $060F ;'VSCROL' COPY
0520 HTIME = $0610 ;'HSPEED' COPY
0530 VTIME = $0611 ;'VSPEED' COPY
0540 HPOINT = $0612 ;SHIFT COUNTER
0550 VPOINT = $0613 ;SHIFT COUNTER
0560 DLINES = $0614 ;LMS OPCODES CNT
0570 COUNT = $0615 ;'DLINES' COPY
0580 TEMP = $0616 ;TEMP STORAGE
0590 TEMP1 = $0617 ;TEMP STORAGE
0600 XHOLD1 = $0618 ;TEMP STORAGE
0610 XHOLD2 = $0619 ;TEMP STORAGE
0620 OPCODE = $061A ;DLIST OPCODE
0630 VBLANK = $061B ;VBLANK VECTOR
0640 OFFSETS = $061D ;OFFSETS TO LMS
0650 STOPALL = $06FF ;HOLD EVERYTHING
1000 .INCLUDE #D:VAR.ASM
1010 ;
1020 ;**********************
1030 ;INITIALIZATION ROUTINE
1040 ;**********************
1050 ;INITIALIZATION ROUTINE
1060 *= $4000
1070 LDA POKMSK ;GET IRQ. INT.
1080 AND #$7F ;NO BREAK KEY
1090 STA POKMSK ;THE BREAK KEY
1100 STA IRQEN ;NO LONGER WORKS
1110 CLD ;CLEAR DECIMAL
1120 PLA ;# OF ARGUEMENTS
1130 PLA ;VBLANK HI/BYTE
1140 STA VBLANK+1
1150 PLA ;VBLANK LO/BYTE
1160 STA VBLANK
1170 PLA ;DLIST HI/BYTE
1180 STA LISTHI
1190 PLA ;DLIST LO/BYTE
1200 STA LISTLO
1210 PLA ;DISCARD
1220 PLA ;GET 'LINES'
1230 STA ALINES
1240 PLA ;DISCARD
1250 PLA ;GET 'MINX'
1260 STA MINX
1270 STA HPOINT ;RESET TO START
1280 PLA ;DISCARD
1290 PLA ;GET 'MAXX'
1300 STA MAXX
1310 PLA ;DISCARD
1320 PLA ;GET 'MINY'
1330 STA MINY
1340 STA VPOINT ;RESET TO START
1350 PLA ;DISCARD
1360 PLA ;GET 'MAXY'
1370 STA MAXY
1380 PLA ;DISCARD
1390 PLA ;GET 'HMAX'
1400 STA HMAX
1410 PLA ;DISCARD
1420 PLA ;GET 'VMAX'
1430 STA VMAX
1440 PLA ;DISCARD
1450 PLA ;GET 'HSPEED'
1460 STA HSPEED
1470 PLA ;DISCARD
1480 PLA ;GET 'VSPEED'
1490 STA VSPEED
1500 PLA ;DISCARD
1510 PLA ;GET 'STICK'
1520 STA STICK
1530 PLA ;DISCARD
1540 PLA ;GET 'HINVERT'
1550 STA HINVERT
1560 PLA ;DISCARD
1570 PLA ;GET 'VINVERT'
1580 STA VINVERT
1590 PLA ;PAGE USAGE HI
1600 STA PAGES+1 ;STORE IT
1610 PLA ;PAGE USAGE LO
1620 STA PAGES ;STORE IT
1630 LDA #$01 ;INITIALIZE RAM
1640 STA HTIME ;COPY OF SPEED
1650 STA VTIME ;TIMERS.
1660 ;
1670 ;*******************************
1680 ;EXAMINE DLIST, NOTE LMS OPCODES
1690 ;*******************************
1700 ;
1710 LDA ZPAGE ;SAVE WHATEVER
1720 PHA ;IS IN MEM. LOC.
1730 LDA ZPAGE+1 ;SAVE WHATEVER
1740 PHA ;IS IN MEM. LOC.
1750 LDA LISTLO ;GET DLIST/LO &
1760 STA ZPAGE ;PUT IN WORKAREA
1770 LDA LISTHI ;GET DLIST/HI &
1780 STA ZPAGE+1 ;PUT IN WORKAREA
1790 LDY #$00 ;SET UP COUNTER
1800 LDX #$00 ;SET UP COUNTER
1810 STX VBIT ;SET UP SCROLL
1820 STX VBIT ;BITS TO ZERO
1830 DLOOK LDA (ZPAGE),Y ;GET DISPLAY
1840 STA OPCODE ;LIST OPCODE
1850 AND #$0F ;IS IT A BLANK
1860 BEQ NEXT ;LINE OPCODE?
1870 LDA OPCODE ;IS IT AN LMS
1880 AND #$40 ;OPCODE?
1890 BEQ NEXT
1900 INY ;IF SO, SKIP
1910 INY ;LMS OPERANDS
1920 LDA OPCODE
1930 AND #$0F ;IS IT A JVB
1940 CMP #$01 ;OPCODE?
1950 BEQ DONE ;IF SO, STOP.
1960 LDA OPCODE ;ARE SCROLL BITS
1970 AND #$30 ;OF OPCODE SET?
1980 BEQ NEXT
1990 TYA ;THIS IS AN LMS
2000 SEC ;OPCODE W/SROLL
2010 SBC #$02 ;BITS SET. SAVE
2020 STA OFFSETS,X ;THE OFFSET.
2030 INX
2040 NEXT INY ;MOVE TO NEXT
2050 BNE DLOOK ;DLIST OPCODE.
2060 DONE STX DLINES ;SAVE # OF LINES
2070 PLA ;RESTORE MEM.
2080 STA ZPAGE+1 ;LOCATION.
2090 PLA ;RESTORE MEM.
2100 STA ZPAGE ;LOCATION.
2110 LDA #$07 ;TELL SYSTEM TO
2120 LDX VBLANK+1 ;SET UP DEF.
2130 LDY VBLANK ;VERTICAL BLANK
2140 JSR SETVBV ;ROUTINE.
2150 RTS ;ALL DONE. BYE!
7000 .INCLUDE #D:VAR.ASM
7010 ;
7020 ;**************************
7030 ;TIME TO HORIZONTAL SCROLL?
7040 ;**************************
7050 ;
7060 *= $5000
7070 LDA STOPALL ;IS IT OKAY TO
7080 BEQ PLUNGE ;EXECUTE?
7090 JMP XITVBV ;NO! BYE!
7100 PLUNGE CLD ;YES, CLEAR DEC.
7110 LDA LISTLO ;TELL ANTIC
7120 STA SDLSTL ;WHERE YOUR NEW
7130 LDA LISTHI ;DISPLAY LIST IS
7140 STA SDLSTL+1
7150 DEC HTIME ;DECREMENT TIMER
7160 BNE ENDIT ;IF <> 0, STOP!
7170 LDA HSPEED ;RESET TIMER
7180 STA HTIME
7190 BEQ ENDIT ;IF = 0, STOP!
7200 ;
7210 ; NOW READ CORRECT JOYSTICK
7220 ;
7230 LDX STICK ;GET POSITION OF
7240 LDA STICK0,X ;RIGHT JOYSTICK
7250 LDY HINVERT ;SHOULD WE IN-
7260 BNE HOPP ;VERT HOR. MOVE
7270 ;
7280 ;SCROLL DIRECTION ISN'T INVERTED
7290 ;
7300 AND #$0C ;SCAN SELECT
7310 CMP #$08 ;JOYSTICK BITS
7320 BEQ HLEFT ;MOVE LEFT
7330 CMP #$04
7340 BEQ HRIGHT ;MOVE RIGHT
7350 BNE ENDIT
7360 ;
7370 ;SCROLL DIRECTION IS INVERTED
7380 ;
7390 HOPP AND #$0C ;SCAN SELECT
7400 CMP #$04 ;JOYSTICK BITS
7410 BEQ HLEFT ;MOVE LEFT
7420 CMP #$08
7430 BNE ENDH ;MOVE RIGHT
7440 ;
7450 ; SCROLL RIGHT
7460 ;
7470 HRIGHT INC HBIT ;INCREMENT RAM
7480 LDA HBIT ;'HSCROL' COPY
7490 CMP HMAX ;IS IT ABOVE
7500 BEQ ENDH ;VALID SCROLL
7510 BCC ENDH ;LIMIT?
7520 LDA HPOINT ;YES! ARE WE AT
7530 CMP MINX ;LEFTMOST BOUND?
7540 BNE HOR5 ;NO, CONTINUE
7550 DEC HBIT ;YES! HALT
7560 BEQ ENDH ;SCROLL & QUIT
7570 BNE ENDH
7580 HOR5 DEC HPOINT ;CONTINUE.
7590 LDA #$FF ;PERFORM BYTE
7600 PHA ;SHIFTING W/HFIX
7610 BMI HFIX ;ROUTINE
7620 HOR55 LDA #$00 ;RESET 'HSCROL'
7630 STA HBIT ;RAM COPY.
7640 BEQ ENDH ;ALL DONE.
7650 ;
7660 ;INC/DEC LMS LO/BYTE OPERANDS
7670 ;
7680 HFIX STA TEMP ;GET BYTES TO
7690 PLA ;INC/DEC LO-BYTE
7700 STA TEMP1 ;OF LMS OPERANDS
7710 LDX ALINES ;GET # OF LINES
7720 DEX ;MINUS ONE
7730 LDY #$00 ;SET INDEX TO 0
7740 HFIX5 LDA (PAGES),Y ;GET OPERAND
7750 CLC
7760 ADC TEMP ;INC/DEC IT
7770 STA (PAGES),Y ;PUT IT BACK
7780 INY ;CHECK FOR WRAP
7790 LDA (PAGES),Y ;AROUND OF LO
7800 ADC TEMP1 ;BYTE INTO HI
7810 STA (PAGES),Y ;BYTE. FIXITUP
7820 INY ;ADJUST POINTER
7830 DEX ;DID WE DO ALL?
7840 BPL HFIX5 ;NO! CONTINUE
7850 LDA TEMP ;YES! DECIDE
7860 BMI HOR55 ;WHO CALLED THIS
7870 BPL HOR66 ;ROUTINE. RETURN
7880 ;
7890 ;INTERMEDIATE BRANCH
7900 ;
7910 ENDIT BEQ ENDH ;NO MATTER WHAT
7920 BNE ENDH ;BRANCH TO END.
7930 ;
7940 ; SCROLL LEFT
7950 ;
7960 HLEFT DEC HBIT ;DEC HSCROL COPY
7970 BPL ENDH ;OUT OF RANGE?
7980 LDA HPOINT ;YES! ARE WE AT
7990 CMP MAXX ;RIGHT BOUND?
8000 BNE HOR6 ;NO! CONTINUE
8010 INC HBIT ;YES! HALT
8020 BEQ ENDH ;SCROLL & QUIT
8030 BNE ENDH
8040 HOR6 INC HPOINT ;NO! PERFORM
8050 LDA #$00 ;BYTE SHIFTING
8060 PHA ;W/HFIX ROUTINE
8070 LDA #$01
8080 BPL HFIX
8090 HOR66 LDA HMAX ;RESET 'HSCROL'
8100 STA HBIT ;RAM COPY
8110 ;
8120 ; TIME TO VERTICAL SCROLL?
8130 ;
8140 ENDH DEC VTIME ;DEC. TIMER
8150 BNE ENDV ;IF <> 0, QUIT
8160 LDA VSPEED ;RESTORE TIMER
8170 STA VTIME ;FOR NEXT TIME
8180 BEQ ENDV ;IF = 0, QUIT
8190 ;
8200 ; READ CORRECT JOYSTICK
8210 ;
8220 LDX STICK ;GET JOYSTICK #
8230 LDA STICK0,X ;GET READING
8240 LDY VINVERT ;IS VER. SCROLL
8250 BNE VOPP ;INVERTED?
8260 ;
8270 ;VERTICAL SCROLL ISN'T INVERTED
8280 ;
8290 AND #$03 ;SCAN SELECT
8300 CMP #$02 ;BITS
8310 BEQ VUP ;SCROLL UP
8320 CMP #$01
8330 BEQ VDOWN ;SCROLL DOWN
8340 BNE ENDV
8350 ;
8360 ;VERTICAL SCROLL IS INVERTED
8370 ;
8380 VOPP AND #$03 ;SCAN SELECT
8390 CMP #$01 ;BITS
8400 BEQ VUP ;SCROLL UP
8410 CMP #$02 ;SCROLL DOWN
8420 BNE ENDV ;DO NADA!
8430 ;
8440 ;SCROLL DOWN
8450 ;
8460 VDOWN DEC VBIT ;DEC VSCROL COPY
8470 BPL ENDV ;IN VALID RANGE?
8480 LDA VPOINT ;NO! ARE WE AT
8490 CMP MINY ;UPPER BOUND?
8500 BNE VER5 ;NO! CONTINUE
8510 INC VBIT ;YES! STOP
8520 BEQ ENDV ;SCROLL & QUIT
8530 BNE ENDV
8540 VER5 DEC VPOINT ;ADJUST VERTICAL
8550 LDA VMAX ;RESET RAM COPY
8560 STA VBIT ;OF 'VSCROL'
8570 BEQ ENDV ;ALL DONE!
8580 BNE ENDV
8590 ;
8600 ;SCROLL UP
8610 ;
8620 VUP INC VBIT ;INC RAM COPY
8630 LDA VBIT ;OF 'VSCROL'
8640 CMP VMAX ;IS IT IN VALID
8650 BEQ ENDV ;RANGE?
8660 BCC ENDV
8670 LDA VPOINT ;NO! ARE WE AT
8680 CMP MAXY ;LOWER BOUND?
8690 BNE VER6 ;NO! CONTINUE
8700 DEC VBIT ;YES! HALT
8710 BEQ ENDV ;SCROLL & QUIT
8720 BNE ENDV
8730 VER6 INC VPOINT ;ADJUST VERTICAL
8740 LDA #$00 ;RESET RAM COPY
8750 STA VBIT ;OF 'VSCROL'
8760 ENDV LDA HBIT ;INSTALL COPY
8770 STA HSCROL ;INTO 'HSCROL'
8780 LDA VBIT ;INSTALL COPY
8790 STA VSCROL ;INTO 'VSCROL'
8800 ;
8810 ;INSTALL PAGE DATA INTO DLIST
8820 ;
8830 FIX LDA DLINES ;GET # OF LMS
8840 BEQ RTS ;OPCODES
8850 STA COUNT ;HOLD ON TO IT
8860 LDA ZPAGE ;SAVE WHATEVER
8870 PHA ;IS IN MEM. LOC.
8880 LDA ZPAGE+1 ;SAVE WHATEVER
8890 PHA ;IS IN MEM. LOC.
8900 LDX #$00 ;SET INDEX TO 0
8910 STX XHOLD1
8920 LDA VPOINT ;VERTICAL POINT
8930 ASL A ;MULTIPLY BY 2
8940 STA XHOLD2 ;HOLD ON TO IT
8950 LDA SDLSTL ;GET LO/DLIST &
8960 STA ZPAGE ;PUT IN WORKAREA
8970 LDA SDLSTL+1 ;GET HI/DLIST &
8980 STA ZPAGE+1 ;PUT IN WORKAREA
8990 HSTUFF LDY XHOLD2 ;PAGE INDEX
9000 LDA (PAGES),Y ;PAGE DATA
9010 PHA ;SAVE IT
9020 LDX XHOLD1 ;OFFSET INDEX
9030 LDA OFFSETS,X ;OFFSET TO LMS
9040 TAY ;HOLD IT
9050 INY ;PLUS ONE
9060 PLA ;GET PAGE DATA
9070 STA (ZPAGE),Y ;PUT IN DLIST
9080 INC XHOLD2 ;INC PAGE INDEX
9090 LDY XHOLD2 ;GET IT AGAIN
9100 LDA (PAGES),Y ;PAGE DATA
9110 PHA ;SAVE IT
9120 LDA OFFSETS,X ;OFFSET TO LMS
9130 TAY ;HOLD IT
9140 INY ;PLUS ONE
9150 INY ;PLUS TWO
9160 PLA ;GET PAGE DATA
9170 STA (ZPAGE),Y ;PUT IN DLIST
9180 INC XHOLD1 ;INC OFFSET INDEX
9190 INC XHOLD2 ;INC PAGE INDEX
9200 DEC COUNT ;ARE WE DONE?
9210 BNE HSTUFF ;NO! KEEP GOING.
9220 PLA ;RESTORE MEMORY
9230 STA ZPAGE+1 ;LOCATION
9240 PLA ;RESTORE MEMORY
9250 STA ZPAGE ;LOCATION
9260 RTS JMP XITVBV ;ALL DONE. LATER!