diff --git a/README.md b/README.md index da92a8e..a721360 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ # vic-sss Commodore VIC20: Software Sprite Stack library using modern 6502 assembler + +I have found that programming for an 8-bit computer is a very challenging, yet +personally rewarding, experience: + +VIC-SSS provides a programmer-friendly API to manage your game's playfield with +software-rendered sprites and other animations for a flicker-free video +experience. On-the-fly custom character manipulations with dual video buffers +accomplish these goals, avoiding the alternative of dedicating all internal RAM +for a smaller, but fully, bit-mapped screen. This API supports both NTSC and +PAL VIC 20 computers, and allows for display modes that change VIC's 22x23 +screen layout. + +The software sprite stack promotes a flicker-free video experience, with the +option by the game programmer to govern frame buffer flips with screen raster +timing. While the VIC 20 computer and its graphics are primitive to begin with, +this API was created to strike a balance between machine and programmer +friendliness – which is what the VIC is all about. The result of that +friendliness makes the code size around 2 kilobytes and requires nearly all of +the internal 4 kilobytes of RAM for graphics display and management. Thus, your +game program will require some form of memory expansion – all examples provided +will run on 8k expansion. + diff --git a/VIC-SSS-MMX.pdf b/VIC-SSS-MMX.pdf new file mode 100755 index 0000000..c6aa0b1 Binary files /dev/null and b/VIC-SSS-MMX.pdf differ diff --git a/ca65-primer/COMPILE.BAT b/ca65-primer/COMPILE.BAT new file mode 100755 index 0000000..29aa2e1 --- /dev/null +++ b/ca65-primer/COMPILE.BAT @@ -0,0 +1,15 @@ +@echo on +ca65.exe --cpu 6502 -t vic20 --listing --include-dir . vic-sss4.s +ca65.exe --cpu 6502 -t vic20 --listing --include-dir . -o sample.o basic-8K.s +ld65.exe -C basic-8k.cfg -Ln sample.sym -m sample.map -o ..\ca65-sprite.prg sample.o vic-sss4.o +@echo off + +choice /C DMV /D M /T 30 /M "[D]ebug, [M]ESS, or [V]ICE? " /N +set CHOICE=%ERRORLEVEL% +cd .. + +if %CHOICE% EQU 1 mess -debug -window -natural -skip_gameinfo -skip_warnings vic20 -ramsize 16k -quik ca65-sprite.prg +if %CHOICE% EQU 2 mess -skip_gameinfo -skip_warnings -newui vic20 -ramsize 16k -quik ca65-sprite.prg +if %CHOICE% EQU 3 xvic -memory 8k -autostart ca65-sprite.prg + +exit diff --git a/ca65-primer/VIC-SSS-MMX.h b/ca65-primer/VIC-SSS-MMX.h new file mode 100755 index 0000000..079c0e1 --- /dev/null +++ b/ca65-primer/VIC-SSS-MMX.h @@ -0,0 +1,252 @@ +;********************************************************************* +; Commodore VIC 20 Software Sprite Stack - MMX Edition +; written by Robert Hurst +; last updated: 30-Oct-2011 +; +; === IMPORTANT === +; required symbols you need to define for your game: +; +;SPRITEDEF4 = $10 ; un-comment this for "repeating" flag usage +;SPRITEDEF5 = $20 ; un-comment this for "ghost" flag usage +;SPRITEDEF6 = $40 ; un-comment this for "collision" flag usage +SPRITEWIDE = 1 ; comment this out to skip 16-bit wide sprites +SPRITEMAX = 16 ; reserves this many SPRITE registers (1-?) +SSSNULL = $A0 ; your character used for a blank background + +;********************************************************************* +; some pertinent VIC 20 symbols +; +RNDSEED = $8B ; -$8F: BASIC RND seed value +JIFFYH = $A0 ; jiffy clock high +JIFFYM = $A1 ; jiffy clock med +JIFFYL = $A2 ; jiffy clock low +DATANEXT = $A6 ; DATASETTE pointer (0-191) +KEYCHARS = $C6 ; number of characters in KEYBUF (0-10) +RVSFLAG = $C7 ; character reverse flag +PLAYROWS = $C8 ; current screen row length (16-24) +CURSOR = $CC ; cursor enable (0=flash) +CRSRCHAR = $CE ; character under cursor +SCRNLINE = $D1 ; pointer to cursor's screen line +CRSRCOL = $D3 ; position of cursor on screen line +PLAYCOLS = $D5 ; current screen line length (16-24) +CRSRROW = $D6 ; screen row where cursor is +COLORLINE = $F3 ; pointer to cursor's color line +INPUT = $0200 ; -$0258: 89-character BASIC INPUT buffer +KEYBUF = $0277 ; -$0280: 10-character keyboard buffer +COLORCODE = $0286 ; current cursor color +CRSRCOLOR = $0287 ; color under cursor +SCRNPAGE = $0288 ; active screen memory page (unexpanded = $1E) +SHIFTMODE = $0291 ; 0=allow, 128=locked +SCROLLFLAG = $0292 ; auto scrolldown flag +ACOPY = $030C ; temp storage for A register +XCOPY = $030D ; temp storage for X register +YCOPY = $030E ; temp storage for Y register +DATASETTE = $033C ; -$03FB: 192-byte tape input buffer +MASK = $8270 ; ROM character $40 - Shift-M (\) +VIC = $9000 ; start of Video Interface Chip registers +MACHINE = $EDE4 ; NTSC=$05, PAL=$0C +STOPKEY = $F770 ; check for STOP key pressed +RESET = $FD22 ; warm startup +CHROUT = $FFD2 ; print character with cursor translation +GETIN = $FFE4 ; get a character from keyboard queue + +;********************************************************************* +; volatile VIC-SSS symbols +; +VECTORBG = $01 ; sprite temp pointer to an image source +DIRTYLINE2 = $59 ; -$70: 24 screen rows for last dirty column +1 +NEWDIRT = $BF ; bit 7=VIDEO1, 6=VIDEO2, 5=PLAYFIELD, 4=STATIC +DIRTYLINE = $D9 ; -$F0: 24 screen rows for starting dirty column +DIRTMAP = $F1 ; pointer to PLAYCOLOR for dirty-bit updates +VECTORFG = $F7 ; sprite temp pointer to an image target +VECTOR1 = $F9 ; sprite temp pointer +VECTOR2 = $FB ; sprite temp pointer +VECTOR3 = $FD ; sprite temp pointer +FPS = $0285 ; number of VIC re-directions every 64-jiffies +PENDING = $0293 ; next video page: $10 or $12 +ACTUAL = $0294 ; save VIC startup video page +VSYNC = $0295 ; set when waiting for vertical sync(s) +VSYNC2 = $0296 ; frames skipped +VCOUNT = $0297 ; current SSSFLIP count +SSSCLIPX = $0298 ; pixels to right border: 8 * (PLAYCOLS + 2) +SSSCLIPY = $0299 ; pixels to bottom border: 8 * (PLAYROWS + 2) +R0 = $029A ; unused temporary register +R1 = $029B ; unused temporary register +R2 = $029C ; unused temporary register +R3 = $029D ; unused temporary register +R4 = $029E ; unused temporary register + +;********************************************************************* +; FRAME REGISTERS +; +VICFRAME1 = $1000 ; first video buffer +VICCOLOR1 = $9400 ; first color buffer +VICFRAME2 = $1200 ; second video buffer +VICCOLOR2 = $9600 ; second color buffer +PLAYFIELD = $1400 ; write-pending screen buffer +PLAYCOLOR = $1600 ; write-pending color buffer (bits 0-3) + ; bit 4 = static cell bit, sprites go behind + ; bit 5 = dirty bit for pending page + ; bit 6 = dirty bit for video page 2 only + ; bit 7 = dirty bit for video page 1 only + +;********************************************************************* +; SPRITE REGISTERS +; +.global SSSBUF ; defaults to $1800, but can be relocated by linker +.global SPRITEBACK ; character code this sprite is in collision with +.global SPRITEBUFH ; pointer within sprite image buffer @ $1800 - $19FF +.global SPRITEBUFL +.global SPRITEC1H ; pointer within sprite display character pool +.global SPRITEC1L +.global SPRITEC2H ; pointer within sprite display character pool +.global SPRITEC2L +.global SPRITECOL ; 4-bit VIC color code +.global SPRITECX ; sprite collision X-coord +.global SPRITECY ; sprite collision Y-coord +.global SPRITEDEF ; matrix definition: + ; bit 0: height 0 = 8px; 1 = 16px + ; bit 1: width 0 = 8px; 1 = 16px + ; bit 2: float Y 0=fixed cell; 1=vertical float + ; bit 3: float X 0=fixed cell; 1=horizontal float + ; bit 4: repeat 0=independent; 1=re-use previous + ; bit 5: ghost 0=merge image; 1=invert image + ; bit 6: collision 0=ignore; 1=detect + ; bit 7: enabled 0=invisible; 1=visible +.global SPRITEH ; number of raster lines (1-16) +.global SPRITEIMGH ; pointer to source graphic for rendering at 0,0 +.global SPRITEIMGL +.global SPRITEX ; horizontal pixel coordinate, visible >0 - 0 - ', $BE +.charmap '?', $BF +.charmap '~', $DE ; PI symbol + diff --git a/ca65-primer/VIC-SSS-MMX.o b/ca65-primer/VIC-SSS-MMX.o new file mode 100755 index 0000000..12ffd44 Binary files /dev/null and b/ca65-primer/VIC-SSS-MMX.o differ diff --git a/ca65-primer/VIC-SSS-MMX.s b/ca65-primer/VIC-SSS-MMX.s new file mode 100755 index 0000000..24d9266 --- /dev/null +++ b/ca65-primer/VIC-SSS-MMX.s @@ -0,0 +1,1436 @@ +;********************************************************************* +; Commodore VIC 20 Software Sprite Stack - MMX Edition +; last updated: 30-Oct-2011 +; written by Robert Hurst +; with contributions from: +; Matt Dawson +;********************************************************************* + + .fileopt author, "Robert Hurst" + .fileopt comment, "Software Sprite Stack" + .fileopt compiler, "VIC 20 ASSEMBLER" + + +;********************************************************************* +; To assemble this source using cc65.org project: +; ca65.exe --cpu 6502 --listing VIC-SSS-MMX.s +; Then link it into your project: +; ld65.exe -C yourlinker.cfg -o yourgame.prg yourgame.o VIC-SSS-MMX.o +; +; See the various .bat files used for working examples. +; + .include "VIC-SSS-MMX.h" + .segment "SPRITE" + + +;********************************************************************* +; Software Sprite Stack INITIALIZATION +; +; MUST BE INVOKED ONCE BEFORE USING ANY OTHER SSS CALL +; Value in COLORCODE will be used to fill the color buffers. +; +SSSINIT: + ; SSS geometry + LDA VIC+$02 + AND #$1F + STA PLAYCOLS + ASL ; CLC + ADC #$04 + ASL + ASL + STA SSSCLIPX + LDA VIC+$03 + AND #$7E + LSR + STA PLAYROWS + ASL ; CLC + ADC #$04 + ASL + ASL + STA SSSCLIPY + ; + LDA #$00 + STA CRSRROW + STA CRSRCOL + TAY + TAX +@sss: TYA + STA sss+1,X + LDA CRSRCOL + STA sss,X + CLC + ADC PLAYCOLS + BCC @cc + INY +@cc: STA CRSRCOL + INX + INX + INC CRSRROW + LDA CRSRROW + CMP PLAYROWS + BNE @sss + ; + ; SSS active / pending video page pointers + LDA #>VICFRAME1 + STA SCRNPAGE + LDA #>VICFRAME2 + STA PENDING + LDA #$40 + STA NEWDIRT + ; + ; kernal init + LDA #$01 + STA RVSFLAG ; character reverse flag + LDA #$80 + STA SHIFTMODE ; locked + LDA #$00 + STA SCROLLFLAG ; disable + ; + ; SPRITE register init + STA SPRITES + ; + ; fill VIC screen / color buffers + LDA #SSSNULL + JSR SSSCLEAR + ; + ; VIC register init + LDA VIC+$02 + AND #$7F ; if $80 enabled, +$0200 to base screen address + STA VIC+$02 + LDA #$CF ; point VIC screen @ $1000 w/ char set @ $1C00 + ; LDA #$C0 ; uncomment for debugging display purposes + STA VIC+$05 + ; + ; FRAME register init + LDY #$00 + STY FPS + STY VSYNC + STY VSYNC2 + STY VCOUNT + JSR SSSFLIP + RTS + + +;********************************************************************* +; Software Sprite Stack IRQ HANDLER +; +; PROGRAM USE OF THIS HANDLER IS OPTIONAL +; +; customize to your liking, i.e., +; - change JMP $EABF here to continue to your custom IRQ handler; or +; - JMP SSSIRQ at the end of your IRQ handler. +; +SSSIRQ: + LDA JIFFYL + AND #%00111111 + BNE @cont + LDX VCOUNT + STX FPS + STA VCOUNT +@cont: + INC VSYNC2 ; frame skipped? + LDA VSYNC + BEQ @fskip ; program is NOT waiting ... + LDX #$00 + SEC + SBC VSYNC2 + STA VSYNC ; save result + BCS @reset ; program wants more than 1 screen refresh + STX VSYNC ; clear wait for vertical sync flag +@reset: STX VSYNC2 ; clear frame skip counter +@fskip: + JMP $EABF + + +;********************************************************************* +; Software Sprite Stack CLEAR SCREEN +; +; Pass A with the character code to fill the PLAYFIELD buffer. +; Value in COLORCODE will be used to fill the PLAYCOLOR buffer. +; +; These changes will go into effect only after a call to SSSFLIP +; +SSSCLEAR: + PHA ;++ save character fill code + LDX #$00 + LDY #$00 + JSR SSSPLOT ; home "cursor" +@reset: TYA + STA DIRTYLINE,X + LDA PLAYCOLS + STA DIRTYLINE2,X + INX + CPX PLAYROWS + BNE @reset + ; +@cls: PLA ;-- + PHA ;++ + JSR SSSPRINT + LDX CRSRCOL + BNE @cls ; loop until column wraps + LDY CRSRROW + BNE @cls ; loop until row wraps, too + PLA ;-- + RTS + + +;********************************************************************* +; Software Sprite Stack PLOT CURSOR IN FRAME BUFFER +; +; SSSPLOT uses PLAYFIELD frame buffer. +; SSSPLOTS uses PENDING frame buffer. +; +; Pass X/Y with coordinate to put cursor. +; +SSSPLOT: + LDA #>PLAYFIELD + STA SCRNLINE+1 + LDA #>PLAYCOLOR + STA COLORLINE+1 + BNE SSSPLOTX +; +; PENDING frame cursor (for writing SPRITE chars) +SSSPLOTS: + LDA PENDING + STA SCRNLINE+1 + ORA #$84 + STA COLORLINE+1 +; +SSSPLOTX: + LDA #>PLAYCOLOR + STA DIRTMAP+1 +@x: CPX PLAYCOLS + BMI @y + LDX PLAYCOLS + DEX +@y: CPY PLAYROWS + BMI @ok + LDY PLAYROWS + DEY +@ok: STX CRSRCOL ; maintain column offset to row + STY CRSRROW ; maintain row number + TYA + ASL + TAY + LDA sss+1,Y + BEQ @top + INC SCRNLINE+1 + INC COLORLINE+1 + INC DIRTMAP+1 +@top: LDA sss,Y + STA SCRNLINE + STA COLORLINE + STA DIRTMAP + LDY CRSRROW + RTS + + +;********************************************************************* +; Software Sprite Stack PRINT TO A FRAME BUFFER +; +; Like SSSPOKE, writes to current cursor, but also advances it to +; the right (with line/screen wrap) upon completion. +; +; All registers are preserved from this call, for common loop use. +; +SSSPRINT: + STX XCOPY ; save index registers + STY YCOPY + JSR SSSPOKE + INC CRSRCOL ; cursor right + LDA CRSRCOL + CMP PLAYCOLS ; moved past right edge? + BCC @fini ; no, all done + LDA #$00 + STA CRSRCOL ; reset to 1st column + INC CRSRROW ; and advance down a row + LDA CRSRROW + CMP PLAYROWS ; moved past bottom edge? + BCS @toprow ; yes, wrap back to top + LDA SCRNLINE ; no, re-calculate new row pointer + CLC + ADC PLAYCOLS + BCC @cc + INC SCRNLINE+1 + INC COLORLINE+1 +@cc: STA SCRNLINE + STA COLORLINE +@fini: LDY CRSRCOL + LDA (COLORLINE),Y + STA CRSRCOLOR + LDA (SCRNLINE),Y + STA CRSRCHAR + LDX XCOPY ; restore index registers + LDY YCOPY + RTS +@toprow: + LDA #$00 + STA CRSRROW + STA SCRNLINE + STA COLORLINE + DEC SCRNLINE+1 + DEC COLORLINE+1 + BNE @fini + + +;********************************************************************* +; Software Sprite Stack PRINT STRING FROM POINTER ON STACK +; +; Will print bytes, until a NULL, following the JSR call here. +; Carriage control and color codes are interpreted. +; +SSSPRINTS: + PLA + STA VECTORFG + PLA + STA VECTORFG+1 + LDY #$01 +@loop: LDA (VECTORFG),Y + BEQ @fini + CMP #$0D + BNE @ctrl + TYA + PHA + LDY CRSRROW + INY + LDX #0 + JSR SSSPLOT + PLA + TAY + JMP @next +@ctrl: CMP #$F0 + BCC @cont ; color code? + AND #$0F ; filter for 16-colors + STA COLORCODE ; 0=blk,1=wht,2=red,3=cyn,4=mag,5=grn,6=blu,7=yel + JMP @next +@cont: JSR SSSPRINT +@next: INY + BNE @loop + INC VECTORFG+1 + BNE @loop + ; +@fini: TYA + CLC + ADC VECTORFG + BCC @cc + INC VECTORFG+1 +@cc: STA VECTORFG + LDA VECTORFG+1 + PHA + LDA VECTORFG + PHA + RTS + + +;********************************************************************* +; Software Sprite Stack READ FROM A FRAME BUFFER +; +; SSSPEEK reads from PLAYFIELD frame buffer. +; SSSPEEKS reads from PENDING frame buffer. +; SSSPEEKXY reads from PLAYFIELD frame buffer, using the sprite pixel +; coordinate system to determine X/Y cursor positioning. +; +; Pass X/Y with the coordinate to put cursor. +; CRSRCHAR and CRSRCOLOR are filled with values under cursor, with +; the former returned in Accumulator. +; +SSSPEEKS: + JSR SSSPLOTS + JMP SSSPEEKX +; +; pixel X/Y +SSSPEEKXY: + CPX #$10 + BCC @fini ; hidden by left border + CPX SSSCLIPX + BCS @fini ; hidden by right border + CPY #$10 + BCC @fini ; above top border + CPY SSSCLIPY + BCC @ok ; below bottom border +@fini: RTS +@ok: TYA + SEC + SBC #$10 + LSR + LSR + LSR + TAY + ; + TXA + SEC + SBC #$10 + LSR + LSR + LSR + TAX +; +SSSPEEK: + JSR SSSPLOT +; +SSSPEEKX: + LDY CRSRCOL + LDA (COLORLINE),Y + STA CRSRCOLOR + LDA (SCRNLINE),Y + STA CRSRCHAR + RTS + + +;********************************************************************* +; Software Sprite Stack WRITE TO A FRAME BUFFER +; +; Pass Accumulator with character code to write to current cursor. +; COLORCODE is used to fill same space with that value. +; +SSSPOKE: + LDY CRSRCOL + STA (SCRNLINE),Y + LDA (DIRTMAP),Y + LDX SCRNLINE+1 + CPX #>PLAYFIELD + BPL @bg + ORA NEWDIRT + STA (DIRTMAP),Y ; update sprite's dirty bits only + .ifdef SPRITEDEF5 + LDA (COLORLINE),Y + BIT COLORCODE + BMI @color ; keep color in place + .endif + LDA COLORCODE ; add color directly to map + JMP @color +@bg: AND #%11000000 ; keep any sprite dirt + ORA #%00100000 ; dirty this PLAYFIELD cell + ORA COLORCODE ; add color +@color: STA (COLORLINE),Y + LDX CRSRROW + LDY CRSRCOL + LDA DIRTYLINE,X + CMP CRSRCOL + BCC @ok ; is dirty seek lower than this write? + STY DIRTYLINE,X ; start looking for dirty cells from here +@ok: TYA + CMP DIRTYLINE2,X + BCC @ok2 ; is dirty seek higher than this write? + INY + STY DIRTYLINE2,X ; stop looking for dirty cells after here +@ok2: RTS + + +;********************************************************************* +; Software Sprite Stack FLIP ACTIVE / PENDING FRAME BUFFERS +; +; Pass Y with the number of vertical sync counts to wait for, or zero +; to make changes visible immediately. +; +; Only the current cursor position is preserved when completed. +; +; If user is holding RUN/STOP key down, the current video frame is +; paused until it is released. +; +SSSFFLIP: + LDA #$00 ; gameplay needs its action to move faster + BIT VSYNC2 + BPL SSSFLIP2 ; do we need to drop a frame? + STA VSYNC2 + RTS + ; +SSSFLIP: + LDA #$00 + STA VSYNC2 +SSSFLIP2: + LDA CRSRROW + PHA ;++ save row + LDA CRSRCOL + PHA ;++ save column + TYA + PHA ;++ save Y (frame count) + ; + ; Phase I: + ; - write any new PLAYFIELD updates to PENDING frame + ; - erase any SPRITE characters from PENDING frame + ; + LDA #%00010000 ; clean TOPFIELD dirt only after this update + JSR SSSCOMMIT + ; + ; Phase II: + ; - render & write SPRITE characters to PENDING frame + ; + JSR SSSUPDATE + ; + ; PHASE III: + ; - signal IRQ to flip video to PENDING frame + ; - wait for the all clear + ; + PLA ;-- restore A (frame count) + TAY + INY ; account for a 'missed' frame + ; INY ; allow for another 'missed' frame + CPY VSYNC2 + BCS @pace ; is game loop & rendering ok? + LDY #$80 ; nope, flag next call to SSSFFLIP + STY VSYNC2 ; to skip rendering/flip altogether + LDA #$00 ; and don't wait for this vsync either +@pace: STA VSYNC ; enable screen to flip +@vsync: LDA VSYNC + BNE @vsync ; and wait for it to occur + BIT VSYNC2 + BMI @ok + STA VSYNC2 ; fast flip, gauge again for next frame + ; +@ok: LDA VIC+$02 + EOR #$80 + STA VIC+$02 ; re-direct VIC to other screen buffer + INC VCOUNT + LDA SCRNPAGE + STA PENDING ; make active screen as pending + EOR #$02 + STA SCRNPAGE ; maintain VIC active video page + ; + ; PHASE IV: + ; - write the same PLAYFIELD updates to new PENDING frame + ; + LDA #%00100000 ; clean PLAYFIELD dirt as part of this update + JSR SSSCOMMIT + ; + PLA ;-- restore X (column) + TAX + PLA ;-- restore Y (row) + TAY + JSR SSSPLOT + ; +@pause: ;JSR STOPKEY + ;BNE @fini + ;LDA #$00 ; clear skipped frame count + ;STA VSYNC2 + ;STA VCOUNT + ; === write any custom PAUSE or RESET code here === + ;PLA + ;PLA + ;.global RESTART + ;JMP RESTART ; label speaks for itself + ; +@fini: RTS + + +;********************************************************************* +; Software Sprite Stack COMMIT CHANGES TO PENDING FRAME BUFFER +; +; pass Accumulator with the PLAYFIELD bit(s) set for cleaning: +; - bit 7 for video #1 +; - bit 6 for video #2 +; - bit 5 for playfield +; - bit 4 for topfield +; +; SPRITE dirt for the PENDING frame is always cleaned. +; +; This is used by SSSFLIP and NOT normally called by user programs. +; +DIRTYMASK = $00 +CLEANER = $01 ; bit 7=VIDEO1, 6=VIDEO2, 5=PLAYFIELD, 4=STATIC +; +SSSCOMMIT: + LDX PENDING + CPX #>VICFRAME1 ; will flip to video #1 next? + BNE @scrn2 + ORA #%10000000 ; erase old sprites from video #1 + BNE @cont +@scrn2: ORA #%01000000 ; erase old sprites from video #2 +@cont: TAY + EOR #$FF ; reverse check to clean + STA CLEANER + TYA + ORA #%00100000 ; but always look for PLAYFIELD dirt + STA DIRTYMASK + TXA + STA VECTOR2+1 + ORA #$84 ; and its COLOR + STA VECTOR3+1 + LDX #$00 + STX VECTOR2 + STX VECTOR3 + LDY #$00 + JSR SSSPLOT ; home "cursor" + ; + LDX #$00 +@forx: LDA DIRTYLINE2,X + STA ACOPY + LDA DIRTYLINE,X ; dirty start column for this row + CMP ACOPY + BCS @nextx ; reached end of this line? + LDY PLAYCOLS + STY NEWDIRT + STY DIRTYLINE,X ; reset this line's seek for next commit + LDY #$00 + STY DIRTYLINE2,X ; reset this line's end for next commit + TAY ; but start this commit from this column +@fory: LDA (COLORLINE),Y + AND #%11100000 ; is this cell dirty for ANY update? + BEQ @nexty ; no, skip it + STY DIRTYLINE2,X ; new ending column for this line + INC DIRTYLINE2,X ; new ending column for this line + CPY NEWDIRT + BCS @more + STY NEWDIRT + STY DIRTYLINE,X ; new starting column for this line +@more: AND DIRTYMASK ; is this cell dirty for THIS update? + BEQ @nexty ; no, skip it + LDA (COLORLINE),Y + STA (VECTOR3),Y ; update color cell + AND CLEANER ; remove this dirt from this cell + STA (COLORLINE),Y + LDA (SCRNLINE),Y + STA (VECTOR2),Y ; update video cell +@nexty: INY + CPY ACOPY + BCC @fory +@nextx: LDA SCRNLINE + CLC + ADC PLAYCOLS + BCC @cc + INC SCRNLINE+1 + INC COLORLINE+1 + INC VECTOR2+1 + INC VECTOR3+1 +@cc: STA SCRNLINE + STA COLORLINE + STA VECTOR2 + STA VECTOR3 + INX + CPX PLAYROWS + BCC @forx + ; + LDA #%10000000 + LDX PENDING + CPX #>VICFRAME2 ; will flip to video #2 next? + BNE @scrn1 + LDA #%01000000 +@scrn1: STA NEWDIRT + RTS + + +;********************************************************************* +; Software Sprite Stack CREATE A NEW SPRITE IN THE LIST +; +; pass Accumulator with the SPRITEDEF value (see HEADER) +; pass Y with the SPRITEH value (1-16) +; returns X with sprite index #0 thru SPRITEMAX-1 +; +SSSCREATE: + STY YCOPY + LDX SPRITES + CPX #SPRITEMAX + BCC @cont + RTS ; sorry, increase SPRITEMAX and re-compile +@cont: + STA SPRITEDEF,X + CPX #$00 + BNE @append ; >1 sprite + ; + ; this is the first sprite in the list ... + LDA #SSSBUF + STA SPRITEBUFL + STY SPRITEBUFH + LDY #$20 ; start at top of custom character + STX SPRITEC1L + STY SPRITEC1H + STX SPRITEC2L + STY SPRITEC2H + BNE @compute +@append: + ; copy prior sprite vectors + LDA SPRITEBUFL-1,X + STA SPRITEBUFL,X + LDA SPRITEBUFH-1,X + STA SPRITEBUFH,X + LDA SPRITEC1L-1,X + STA SPRITEC2L,X + LDA SPRITEC1H-1,X + STA SPRITEC1H,X + STA SPRITEC2H,X + .ifdef SPRITEDEF4 + ; repeating sprite? + LDA SPRITEDEF,X + AND #SPRITEDEF4 + BNE @same + .endif + ; no, allocate new image and character buffers + LDA SPRITEDEF-1,X + AND #$0F + TAY + LDA sssALLOC,Y + CLC + ADC SPRITEBUFL,X + BCC @cc + INC SPRITEBUFH,X +@cc: STA SPRITEBUFL,X +@compute: + LDA SPRITEDEF,X + AND #$0F + TAY + LDA sssALLOC,Y + STA sssBYTES + ; vector#2 into custom char set + LDA SPRITEC2L,X + SEC + SBC sssBYTES ; account for its entire buffer size + BCS @cc1 + DEC SPRITEC2H,X + DEC SPRITEC1H,X +@cc1: STA SPRITEC2L,X + STA SPRITEC1L,X + ; vector#1 into custom char set + SEC + SBC sssBYTES ; account for its entire buffer size + BCS @cc2 + DEC SPRITEC1H,X +@cc2: STA SPRITEC1L,X + JMP @new + ; keep repeating sprite pointing to same custom characters +@same: LDA SPRITEC1L-1,X + STA SPRITEC1L,X + LDA SPRITEC2L-1,X + STA SPRITEC2L,X + LDA SPRITEC2H-1,X + STA SPRITEC2H,X +@new: LDA YCOPY + STA SPRITEH,X + .ifdef SPRITEDEF6 + LDA #SSSNULL ; init with nothing in contact + STA SPRITEBACK,X + .endif + LDA #$00 + .ifdef SPRITEDEF6 + STA SPRITECX,X ; init collision X-coord + STA SPRITECY,X ; init collision Y-coord + .endif + STA SPRITEX,X ; sprite is not in visible area to start + STA SPRITEY,X + STA SPRITEZ,X ; all flags off + LDX SPRITES ; return this new sprite # as initialized + STX sssNUM + INC SPRITES ; account for the new sprite allocated + RTS + + +;********************************************************************* +; Software Sprite Stack SELECT A SPRITE TO MANIPULATE +; +; pass X index with the SPRITES number (0 - = outside right border visible range + LDA SPRITEY,X + BEQ @redraw ; 0 = outside top border visible range + CMP SSSCLIPY + BCS @redraw ; >= outside bottom border visible range + .endif + + ; preset sprite image buffer: + ; VECTOR1 = pointer to top-left within sprite matrix + ; VECTOR2&3 = pointer to adjacent chars, as necessary +@own: ; + JSR SSSUSE + LDA SPRITEBUFH,X + STA VECTOR1+1 + STA VECTOR2+1 + + .ifdef SPRITEWIDE + STA VECTOR3+1 + .endif + + LDA SPRITEBUFL,X + STA VECTOR1 + CLC + ADC sssNEXT + BCC @cc1 + INC VECTOR2+1 + + .ifdef SPRITEWIDE + INC VECTOR3+1 + .endif + +@cc1: STA VECTOR2 + + .ifdef SPRITEWIDE + CLC + ADC sssNEXT + BCC @cc2 + INC VECTOR3+1 +@cc2: STA VECTOR3 + .endif + + .ifdef SPRITEDEF5 + LDY #$11 ; ORA opcode + LDA SPRITEDEF,X + AND #SPRITEDEF5 ; ghost image? + BEQ @bit + LDY #$51 ; EOR opcode +@bit: TYA + STA @OP1 + STA @OP2 + EOR #$40 ; swap opcode + STA @OP3 + .endif + + ; branch on make control flags + LDA SPRITEZ,X + ASL + PHA ; ++ + BCC @copy ; $80 - (re)make buffered image + JSR @Make +@copy: PLA ; -- + ASL + BCC @matrix ; $40 - copy buffered image + JSR @Copy +@matrix: + JSR @Display ; display sprite matrix + JMP @loop +@redraw: + LDA SPRITEZ,X + AND #%11 + ORA #%11110000 ; make + copy/merge + null bg + clipped fg + STA SPRITEZ,X +@loop: + INC sssNUM + LDX sssNUM + JMP @do + ; + ; INIT PHASE + ; ---------- + ; (re)make this sprite's image buffer +@Make: ; + LDA SPRITEH,X + STA sssXFER ; sprite image raster count + LDA SPRITEY,X + AND #$07 + CLC + ADC sssXFER + STA sssDY ; 1st raster below image + ; + ; VECTORBG = pointer to your compact source image + LDA SPRITEIMGL,X + STA VECTORBG + LDA SPRITEIMGH,X + STA VECTORBG+1 + LDA sssNEXT + STA sssLINENUM ; this many raster lines to copy + CMP sssDY + BCS @Mloop ; fits within height of sprite + LDA sssDY + SEC + SBC sssLINENUM + STA ACOPY ; compute how many rasters to clip + LDA sssXFER + SBC ACOPY + STA sssXFER ; clip image within sprite height +@Mloop: + LDA #$00 ; erase raster registers + STA sssROR1 + STA sssROR2 + + .ifdef SPRITEWIDE + STA sssROR3 + .endif + + DEC sssLINENUM + LDY sssLINENUM + LDA sssXFER + BEQ @Mcopy ; no more rasters to copy - zero them + CPY sssDY + BCS @Mcopy ; below sprite image - zero this raster +@Mxfer: + DEC sssXFER ; copying sprite image rasters + LDY sssXFER + LDA (VECTORBG),Y + STA sssROR1 + LDA SPRITEDEF,X + AND #%00000010 + BEQ @Mxfer2 ; 16w ? + LDY #$08 + LDA SPRITEDEF,X + LSR + BCC @Monly8 ; 16h ? + LDY #$10 +@Monly8: + TYA + CLC + ADC sssXFER + TAY + LDA (VECTORBG),Y + STA sssROR2 ; load adjacent register +@Mxfer2: + LDA SPRITEX,X + AND #$07 + BEQ @Mcopy + STA sssDX +@Mx2: LSR sssROR1 + ROR sssROR2 ; shift into image overflow register #1 + + .ifdef SPRITEWIDE + ROR sssROR3 ; shift into image overflow register #2 + .endif + + DEC sssDX + BNE @Mx2 +@Mcopy: + LDX #$01 + LDY sssLINENUM + LDA sssROR1 + STA (VECTOR1),Y + CPX sssX + BEQ @Mnext + LDA sssROR2 ; write image overflow register #1 + STA (VECTOR2),Y + + .ifdef SPRITEWIDE + INX + CPX sssX + BEQ @Mnext + LDA sssROR3 ; write image overflow register #2 + STA (VECTOR3),Y + .endif + +@Mnext: + LDX sssNUM + LDA sssLINENUM + BEQ @Mfini + JMP @Mloop +@Mfini: RTS + ; + ; PHASE II + ; -------- + ; copy/merge buffered image with background into + ; sprite character matrix +@Copy: ; + LDA SPRITEZ,X + AND #%11 + EOR #%1 ; flip to other character set + STA SPRITEZ,X + AND #%1 + BNE @Cfb2 + LDA SPRITEC1L,X + STA VECTORFG + LDA SPRITEC1H,X + STA VECTORFG+1 + BNE @Ccopy +@Cfb2: LDA SPRITEC2L,X + STA VECTORFG + LDA SPRITEC2H,X + STA VECTORFG+1 +@Ccopy: + LDA SPRITEX,X + STA sssDX + LDA sssNEXT + STA sssLINE + LDY #0 + STY sssCHAR + STY sssLINENUM + STY ACOPY +@Cdocol: + LDX sssNUM + LDA SPRITEY,X + STA sssDY +@Cbgimage: + LDX sssNUM + LDA SPRITEZ,X + AND #%10 + BNE @Cjmp ; fast copy? + LDX sssDX + LDY sssDY + JSR SSSREAD + CMP #SSSNULL + BNE @Cmore +@Cjmp: JMP @Ccpfast +@Cmore: + LDX sssNUM + LDA CRSRCOLOR + AND #%10000 + BEQ @Ccont ; static cell? + LDA SPRITEZ,X + ORA #%10000 ; flag that sprite's foreground is clipped + STA SPRITEZ,X + JMP @Cnextrow ; don't bother merging with a background +@Ccont: + + .ifdef SPRITEDEF4 + LDA SPRITEDEF,X + AND #SPRITEDEF4 + BNE @Cjmp + .endif + + LDA CRSRCHAR + JSR SSSIMAGE + STX VECTORBG + STY VECTORBG+1 + LDX #0 + STX sssXFER + INC sssCHAR ; flag that there is something behind this sprite + + .ifdef SPRITEDEF6 + LDY sssNUM + LDA SPRITEZ,Y + AND #%1000 + BNE @Ccploop ; already a collision? + LDA SPRITEDEF,Y + AND #SPRITEDEF6 + BNE @Ccploopx ; collision detection enabled? + .endif + +@Ccploop: + LDY sssXFER ; from ... + LDA (VECTORBG),Y + LDY sssLINENUM ; to ... +@OP1: ORA (VECTOR1),Y ; opcode modification #1: ORA / EOR + STA (VECTORFG),Y +@Ccpnxt: + INC sssLINENUM + INC sssXFER + INX + CPX #8 + BNE @Ccploop + BEQ @Cnextrow + + .ifdef SPRITEDEF6 +@Ccploopx: + LDY sssXFER ; from ... + LDA (VECTORBG),Y + PHA ;++ + LDY sssLINENUM ; to ... +@OP2: ORA (VECTOR1),Y ; opcode modification #2: ORA / EOR + STA (VECTORFG),Y + PLA ;-- +@OP3: EOR (VECTOR1),Y ; opcode modification #3: ORA / EOR + CMP (VECTORFG),Y + BEQ @Cnohit ; any overlapping pixel(s)? + LDY sssNUM + LDA sssDX + STA SPRITECX,Y ; save X sprite coord of what was hit + LDA sssDY + STA SPRITECY,Y ; save Y sprite coord of what was hit + LDA CRSRCHAR ; save character code of what was hit + STA SPRITEBACK,Y + LDA SPRITEZ,Y + ORA #%1000 ; sprite-pixel collision with non-static cell + STA SPRITEZ,Y + BNE @Ccpnxt ; resume normal copy/merge operation +@Cnohit: + INC sssLINENUM + INC sssXFER + INX + CPX #8 + BNE @Ccploopx + BEQ @Cnextrow + .endif + + ; faster copy, because there is no backgound to merge with ... +@Ccpfast: + LDX #8 + LDY sssLINENUM +@Ccploop2: + LDA (VECTOR1),Y + STA (VECTORFG),Y + INY + DEX + BNE @Ccploop2 + STY sssLINENUM +@Cnextrow: + LDA sssDY + CLC + ADC #8 + STA sssDY + LDY sssLINENUM + CPY sssLINE + BEQ @Cnextcol + JMP @Cbgimage +@Cnextcol: + LDA ACOPY + CLC + ADC sssNEXT + STA ACOPY + STA sssLINENUM + LDA sssLINE + CLC + ADC sssNEXT + STA sssLINE + LDA sssDX + CLC + ADC #$08 + STA sssDX + DEC sssX + BEQ @Cdone + JMP @Cdocol +@Cdone: + LDA sssCHAR + BNE @Cfini + LDX sssNUM ; all null background, if no new changes occur + LDA SPRITEZ,X ; don't do a merge on next flip either + ORA #$20 ; enable this sprite to be re-used as-is + STA SPRITEZ,X ; on next frame flip +@Cfini: RTS + ; + ; PHASE III + ; --------- + ; display sprite character matrix + ; by row, then by column +@Display: + LDX sssNUM + + .ifdef SPRITEDEF4 + LDA SPRITEDEF,X + ASL + BCC @Cfini ; sprite is disabled + .endif + + JSR SSSUSE + DEC sssY + DEC sssX + LDA SPRITEX,X + STA sssDX + LDA SPRITECOL,X + STA COLORCODE + + .ifdef SPRITEDEF5 + LDA SPRITEDEF,X + AND #SPRITEDEF5 ; ghost mode? + BEQ @Dok + LDA COLORCODE + ORA #$80 + STA COLORCODE ; flag to keep PLAYFIELD colored cells + .endif + +@Dok: LDA SPRITEZ,X + AND #%1 + BNE @Dfb2 ; which character set to use? + LDA SPRITEC1L,X + STA VECTORFG + LDA SPRITEC1H,X + BNE @Dchar +@Dfb2: LDA SPRITEC2L,X + STA VECTORFG + LDA SPRITEC2H,X +@Dchar: STA VECTORFG+1 + SEC + SBC #$1C ; starting page of custom chars + ASL + ASL + ASL + ASL + ASL ; x32 + STA sssCHAR + LDA VECTORFG + LSR + LSR + LSR ; /8 + ADC sssCHAR + STA sssCHAR ; start with this custom character +@Dcol: + LDA sssY + STA sssLINENUM + LDX sssNUM + LDA SPRITEY,X + STA sssDY ; custom character row (0, 1?, 2?) +@Drow: + LDA sssCHAR + LDX sssDX + LDY sssDY + JSR SSSWRITE ; display it +@Dskip: + INC sssCHAR ; account for it, even if it is not displayed + LDA sssLINENUM + BEQ @Dnrow + DEC sssLINENUM + LDA sssDY + CLC + ADC #8 + STA sssDY ; next Y-pixel + JMP @Drow +@Dnrow: + LDA sssDX + CLC + ADC #8 + STA sssDX + LDA sssX + BEQ @Dncol + DEC sssX + JMP @Dcol +@Dncol: + RTS + + +;********************************************************************* +; Software Sprite Stack GET IMAGE ADDRESS FROM A CHARACTER +; +; This is used by SSSUPDATE and NOT normally called by user programs. +; +; Pass A with the character code. +; returns X/Y as a pointer to its image source. +; +SSSIMAGE: + TAY + ASL + ASL + ASL ; x8 + TAX ; save image low byte + TYA + ROL ; set carry bit + LDY #$00 ; point to custom chars + BCC @cont ; is character reversed? + INY +@cont: ROL + ROL + ROL + AND #%00000011 + ORA @vic,Y ; prepend page pointer + TAY ; save image high byte + RTS + ; VIC custom or ROM characters +@vic: .byte $1C, $80 + + +;********************************************************************* +; Software Sprite Stack READ FROM PENDING FRAME BUFFER +; +; This is used by SSSUPDATE and NOT normally called by user programs. +; +; Pass X/Y with a sprite pixel coordinate. +; CURSOR is re-plotted to this location. +; returns Accumulator with character code from PENDING frame buffer, +; or a SPACE if the coordinate is outside the screen borders. +; returns X/Y cell coordinates. +; +SSSREAD: + LDA #SSSNULL ; default to an empty background + CPX #$10 + BCC @fini ; hidden by left border + CPX SSSCLIPX + BCS @fini ; hidden by right border + CPY #$10 + BCC @fini ; above top border + CPY SSSCLIPY + BCS @fini ; below bottom border + ; + LDA PENDING + STA SCRNLINE+1 + LDA #>PLAYCOLOR + STA COLORLINE+1 + ; + TYA + SEC + SBC #$10 + LSR + LSR + LSR ; /8 + TAY + STY CRSRROW + ; + TXA + SEC + SBC #$10 + LSR + LSR + LSR ; /8 + STA CRSRCOL + ; +@ok: TYA + ASL + TAY + LDA sss+1,Y + BEQ @top + INC SCRNLINE+1 + INC COLORLINE+1 +@top: LDA sss,Y + STA SCRNLINE + STA COLORLINE + LDY CRSRCOL + LDA (COLORLINE),Y ; read from pending color buffer + STA CRSRCOLOR + LDA (SCRNLINE),Y ; read from pending video buffer + STA CRSRCHAR + LDY CRSRROW +@fini: RTS + + +;********************************************************************* +; Software Sprite Stack WRITE TO PENDING FRAME BUFFER +; +; This is used by SSSUPDATE and NOT normally called by user programs. +; +; Pass X/Y with a sprite pixel coordinate. +; Pass A with SPRITE character code to write to PENDING screen buffer. +; COLORCODE is used to fill same space with that value. +; +; Write does not occur if X/Y lie outside the screen borders. +; +SSSWRITE: + CPX #$10 + BCC @fini ; hidden by left border + CPX SSSCLIPX + BCS @fini ; hidden by right border + CPY #$10 + BCC @fini ; above top border + CPY SSSCLIPY + BCS @fini ; below bottom border + PHA ;++ + JSR SSSPEEKXY + LDA CRSRCOLOR + AND #$10 ; static cell? + BEQ @ok + PLA ; yes, don't overwrite it! + RTS + ; +@ok: LDX CRSRCOL + LDY CRSRROW + JSR SSSPLOTS + PLA ;-- + JSR SSSPOKE +@fini: RTS + + +;********************************************************************* +; Software Sprite Stack PROTECTED WRITE TO PENDING FRAME BUFFER +; +; Pass X/Y with a screen cell coordinate. +; Pass A with character code to write to PENDING screen buffer. +; COLORCODE is used to fill same space with that value. +; +SSSCELL: + PHA ;++ + LDA NEWDIRT + STA $01 + LDA #$10 + STA NEWDIRT + JSR SSSPLOTS + PLA ;-- + JSR SSSPOKE + LDA $01 + STA NEWDIRT + RTS + diff --git a/ca65-primer/basic+8k.cfg b/ca65-primer/basic+8k.cfg new file mode 100755 index 0000000..159bb06 --- /dev/null +++ b/ca65-primer/basic+8k.cfg @@ -0,0 +1,29 @@ +# VIC 20 BASIC startup, with at least 8k expansion + +MEMORY { + ZP: start = $0000, size = $0100, type = rw; + RAM0: start = $0400, size = $0C00, type = rw; + RAM: start = $11FF, size = $0601, type = rw, fill = yes; + BUF: start = $1800, size = $0400, type = rw, fill = yes; + CHAR: start = $1C00, size = $0400, type = rw, fill = yes; + RAM1: start = $2000, size = $2000, type = rw; + RAM2: start = $4000, size = $2000, type = rw; + RAM3: start = $6000, size = $2000, type = rw; + ROM1: start = $A000, size = $1000, type = ro; + ROM2: start = $B000, size = $1000, type = ro; +} + +SEGMENTS { + BASIC: load = RAM, type = ro, define = yes, optional = no; + STARTUP: load = RAM, type = ro, define = yes, optional = no; + SSSBUF: load = BUF, type = rw, define = yes, optional = no; + MYCHAR: load = CHAR, type = rw, define = yes, optional = no; + CODE: load = RAM1, type = ro, define = yes, optional = no; + SPRITE: load = RAM1, type = ro, define = yes, optional = no; + RODATA: load = RAM1, type = ro, define = yes, optional = no; +} + +# with at least 8K memory expansion, start here ... +FEATURES { + STARTADDRESS: default = $11FF; +} diff --git a/ca65-primer/basic.o b/ca65-primer/basic.o new file mode 100755 index 0000000..1b2ded8 Binary files /dev/null and b/ca65-primer/basic.o differ diff --git a/ca65-primer/basic.s b/ca65-primer/basic.s new file mode 100755 index 0000000..cff24e0 --- /dev/null +++ b/ca65-primer/basic.s @@ -0,0 +1,192 @@ +;********************************************************************* +; COMMODORE VIC 20 BOOT USING BASIC 2.0 +; written by Robert Hurst +; updated version: 30-Oct-2011 +; + .fileopt author, "your name" + .fileopt comment, "your program name" + .fileopt compiler, "VIC 20 ASSEMBLER" + + .include "VIC-SSS-MMX.h" + +;********************************************************************* +; Commodore BASIC 2.0 program +; +; LOAD "YOUR PROGRAM.PRG",8 +; RUN +; + .segment "BASIC" + + .word RUN ; load address +RUN: .word @end ; next line link + .word 2010 ; line number + .byte $9E ; BASIC token: SYS + .byte <(MAIN / 1000 .mod 10) + $30 + .byte <(MAIN / 100 .mod 10) + $30 + .byte <(MAIN / 10 .mod 10) + $30 + .byte <(MAIN / 1 .mod 10) + $30 + .byte 0 ; end of line +@end: .word 0 ; end of program + +;********************************************************************* +; Starting entry point for this program +; + .segment "STARTUP" + +MAIN: + LDX $FFFC + LDY $FFFD + STX $0318 + STY $0319 ; enable RESTORE key as RESET + LDA MACHINE + CMP #$05 + BNE PAL + ; + ; NTSC setup +NTSC: LDX #<@NTSC ; load the timer low-byte latches + LDY #>@NTSC + LDA #$69 ; raster line 210/211 + BNE IRQSYNC +@NTSC = $4243 ; (261 * 65 - 2) + ; + ; PAL setup +PAL: LDX #<@PAL ; load the timer low-byte latches + LDY #>@PAL + LDA #$76 ; raster line 228/229 +@PAL = $5686 ; (312 * 71 - 2) + ; +IRQSYNC: + CMP VIC+$04 + BNE IRQSYNC + STX $9126 ; load T1 latch low + STY $9125 ; load T1 latch high, and transfer both to T1 counter + + +;********************************************************************* +; Now that all the VIC startup initialization stuff is completed, +; you can append one-time startup code/data here, i.e., like a splash +; title screen. Then, you must jump to your CODE segment, linked +; outside of VIC's internal RAM address space ... +; +RUNONCE: + ; + ;==== INSERT ANY ADDITIONAL STARTUP CODE HERE ==== + ; + JMP RESTART + + +;********************************************************************* +; VIC Software Sprite Stack 2010 (VIC-SSS-MMX) +; +; The above BASIC loader will be overwritten by SSS upon its +; initialization (SSSINIT). The linker will fill this reserved space +; with values used for the dual video frame buffers, play field, and +; the sprite image buffers and registers: 4096 - 6207 ($1000 - $1BFF) +; +; $1000 - $11FF VICFRAME1 - first video buffer +; $1200 - $13FF VICFRAME2 - second video buffer +; $1400 - $15FF PLAYFIELD - write-pending screen buffer +; $1600 - $17FF PLAYCOLOR - write-pending color / dirty buffer +; $1800 - $1BFF Sprite image buffers & registers +; + .segment "SSSBUF" + +SSSBUF: .res 64 * 8 ; this can be resized as required -- + ; if all 64-chars are used by sprites, that + ; exhausts all 128 custom characters for + ; double-buffering (x2) +; +; SPRITE REGISTERS (17) +; +SPRITEBACK: .res SPRITEMAX ; 1st char this sprite is in collision with +SPRITEBUFH: .res SPRITEMAX ; pointer within sprite image buffer +SPRITEBUFL: .res SPRITEMAX ; pointer within sprite image buffer +SPRITEC1H: .res SPRITEMAX ; pointer within sprite display character pool +SPRITEC1L: .res SPRITEMAX ; pointer within sprite display character pool +SPRITEC2H: .res SPRITEMAX ; pointer within sprite display character pool +SPRITEC2L: .res SPRITEMAX ; pointer within sprite display character pool +SPRITECOL: .res SPRITEMAX ; 4-bit VIC color code +SPRITECX: .res SPRITEMAX ; sprite collision X-coord +SPRITECY: .res SPRITEMAX ; sprite collision Y-coord +SPRITEDEF: .res SPRITEMAX ; function/matrix definition (see explanation below) +SPRITEH: .res SPRITEMAX ; number of raster lines (1-16) +SPRITEIMGH: .res SPRITEMAX ; pointer to source graphic for rendering at 0,0 +SPRITEIMGL: .res SPRITEMAX ; pointer to source graphic for rendering at 0,0 +SPRITEX: .res SPRITEMAX ; horizontal pixel coordinate, visible >0 - 0 - SSSIRQ + STX $0314 + STY $0315 + CLI + ; + LDY #$0A ; row 11 + LDX #$08 ; col 9 + JSR SSSPLOT ; cursor moves here + JSR SSSPRINTS + .byte $F2 ; red text + .asciiz "HELLO!" + LDY #$00 ; immediate + JSR SSSFLIP + ; +@init: LDA #%10001100 ; 8x8 sprite floats X-Y + LDY #8 + JSR SSSCREATE + LDA #4 ; magenta + LDX #<$8000 + LDY #>$8000 + JSR SSSANIM + LDA #%10001100 ; 8x8 sprite floats X-Y + LDY #8 + JSR SSSCREATE + LDA #0 ; black + LDX #<$8000 + LDY #>$8000 + JSR SSSANIM + ; +@loop: LDY #1 ; wait for vertical sync + JSR SSSFLIP + LDX #$00 + JSR SSSUSE + LDA SPRITEY,X + TAY + INY + LDA SPRITEX,X + TAX + INX + JSR SSSMOVEXY + LDA SPRITEX,X + BNE @kb + LDA SPRITEIMGL,X + CLC + ADC #$08 + STA SPRITEIMGL,X +@kb: + lda SPRITEY + sta SPRITEY+1 + lda SPRITEX + clc + adc #$10 + sta SPRITEX+1 + ldx #1 + jsr SSSTOUCH + JSR STOPKEY + BNE @loop +@fini: JMP RESET ; bye-bye! diff --git a/ca65-primer/ca65.exe b/ca65-primer/ca65.exe new file mode 100755 index 0000000..42856c0 Binary files /dev/null and b/ca65-primer/ca65.exe differ diff --git a/ca65-primer/compile.sh b/ca65-primer/compile.sh new file mode 100755 index 0000000..2289491 --- /dev/null +++ b/ca65-primer/compile.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# + +TITLE=ca65-sprite + +set -o xtrace +ca65 --cpu 6502 --listing --include-dir . basic.s +ca65 --cpu 6502 --listing --include-dir . $TITLE.s +ca65 --cpu 6502 --listing --include-dir . VIC-SSS-MMX.s +ld65 -C basic+8k.cfg -Ln $TITLE.sym -m $TITLE.map -o $TITLE.prg basic.o $TITLE.o VIC-SSS-MMX.o +set +o xtrace + +echo -n "Press RETURN: " && read N + +# UNCOMMENT YOUR CHOICE OF EMULATORS ... + +#mess -debug vic20 -ramsize 16k -quik $TITLE.prg +#xvic -memory 8k -autostart $TITLE.prg + +exit + diff --git a/ca65-primer/da65.exe b/ca65-primer/da65.exe new file mode 100755 index 0000000..1b9f700 Binary files /dev/null and b/ca65-primer/da65.exe differ diff --git a/ca65-primer/demos/bigdudes.prg b/ca65-primer/demos/bigdudes.prg new file mode 100755 index 0000000..4fdcfa4 Binary files /dev/null and b/ca65-primer/demos/bigdudes.prg differ diff --git a/ca65-primer/demos/bigdudes.s b/ca65-primer/demos/bigdudes.s new file mode 100755 index 0000000..6497393 --- /dev/null +++ b/ca65-primer/demos/bigdudes.s @@ -0,0 +1,260 @@ +;********************************************************************* +; DEMO featuring VIC Software Sprite Stack +; written by Robert Hurst +; updated version: 5-May-2009 +;********************************************************************* +; +; See each .bat file for compile, link, and go instructions. +; + .include "VIC-SSS-MMX.h" + .segment "CODE" + +CTRLKEYS = $028D +; + JSR SSSINIT ; must occur BEFORE re-directing IRQ +; +; implement my interrupt vector + SEI + LDX #SSSIRQ + STX $0314 + STY $0315 + CLI + ; + LDA #$1B + STA VIC+$0F + LDA #$06 + STA COLORCODE + JSR ALERT + LDY #$01 + JSR SSSFLIP + ; + LDA #%10001111 ; 16x16 sprite floats X-Y + LDY #$10 ; height + JSR SSSCREATE + LDA #$02 ; red + LDX #BIGRED + JSR SSSANIM + ; + LDA #%10001111 ; 16x16 sprite floats X-Y + LDY #$10 ; height + JSR SSSCREATE + LDA #$05 ; green + LDX #BIGDUDE + JSR SSSANIM + ; + LDX #$00 + JSR SSSUSE + LDA PLAYCOLS + ASL + TAX + LDA PLAYROWS + ASL + TAY + JSR SSSMOVEXY + ; + LDX #$01 + JSR SSSUSE + LDX #$10 + LDY #$10 + JSR SSSMOVEXY + ; + LDA #$00 + STA R0 + STA R1 + ; +@loop: + LDX #$00 + JSR SSSUSE + LDA SPRITEY,X + TAY + LDA SPRITEX,X + AND #$07 + BNE @redx + INY +@redx: LDA SPRITEX,X + TAX + INX + JSR SSSMOVEXY + ; +@green: + LDX #$01 + JSR SSSUSE + LDA SPRITEX,X + STA XCOPY + LDA SPRITEY,X + STA YCOPY + LDA R0 + BNE @s2 + LDA SPRITEX,X + CLC + ADC #$10 + CMP SSSCLIPX + BCS @x2 + INC XCOPY + BNE @gogreen +@x2: LDA SPRITEY,X + CLC + ADC #$18 + CMP SSSCLIPY + BCS @y2 + INC YCOPY + BNE @gogreen +@y2: INC R0 +@s2: LDA SPRITEX,X + CMP #$11 + BCC @x0 + DEC XCOPY + BNE @gogreen +@x0: LDA SPRITEY,X + CMP #$11 + BCC @y0 + DEC YCOPY + BNE @gogreen +@y0: DEC R0 + BEQ @green +@gogreen: + LDX XCOPY + LDY YCOPY + JSR SSSMOVEXY + ; +@flip: LDY #$02 ; keeps a fair consistent pace, without tearing + JSR SSSFFLIP + ; +@user: LDA CTRLKEYS + AND #$02 ; holding C= key down, bypass cartridge + BNE END + JMP @loop + +;********************************************************************* +; That's all folks +; +END: + JMP RESET + +ALERT: + LDY PLAYROWS + DEY + TYA + LDX #PAUSE + JSR STATUS + LDX #$00 + LDY PLAYROWS + DEY + DEY + JSR SSSPLOT +@line: LDA #$D2 + JSR SSSPRINT ; render a line + LDY CRSRCOL ; above the message + BNE @line + LDY #$01 + JSR SSSFLIP + RTS + +NOSTATUS: + LDY PLAYROWS + DEY + LDX #ERASE +STATUS: + STX VECTORBG + STY VECTORBG+1 + LDX #$00 + TAY + JSR SSSPLOT + ; + LDY #$00 +@loop: LDA (VECTORBG),Y + BNE @print + INC CRSRCOL + BNE @next +@print: JSR SSSPRINT +@next: LDY CRSRCOL + BNE @loop + RTS + +;********************************************************************* + .segment "RODATA" +; +ERASE: .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 +; PRESS C= KEY TO CONT +PAUSE: .byte $00,$90,$92,$85,$93,$93,$A0,$83,$BD,$A0,$8B,$85,$99,$A0,$94,$8F,$A0,$83,$8F,$8E,$94,$00 + +;********************************************************************* +; SPRITE DATA +; +BIGRED: + .byte %00001111 + .byte %00111111 + .byte %01111111 + .byte %11111111 + .byte %11100111 + .byte %11000011 + .byte %11100111 + .byte %11111111 + + .byte %11111111 + .byte %11111111 + .byte %11110011 + .byte %11001100 + .byte %11111111 + .byte %11111111 + .byte %11001100 + .byte %10001000 + + .byte %11110000 + .byte %11111100 + .byte %11111110 + .byte %11111111 + .byte %11100111 + .byte %11000011 + .byte %11100111 + .byte %11111111 + + .byte %11111111 + .byte %11111111 + .byte %11001111 + .byte %00110011 + .byte %11111111 + .byte %11111111 + .byte %00110011 + .byte %00010001 +BIGDUDE: + .byte %00001111 + .byte %00111111 + .byte %11111111 + .byte %11111111 + .byte %11000011 + .byte %11000011 + .byte %11000011 + .byte %11111111 + + .byte %11111111 + .byte %11111111 + .byte %11001111 + .byte %11110000 + .byte %11111111 + .byte %11111111 + .byte %11001100 + .byte %10001000 + + .byte %11110000 + .byte %11111100 + .byte %11111111 + .byte %11111111 + .byte %11000011 + .byte %11000011 + .byte %11000011 + .byte %11111111 + + .byte %11111111 + .byte %11111111 + .byte %11110011 + .byte %00001111 + .byte %11111111 + .byte %11111111 + .byte %00110011 + .byte %00010001 diff --git a/ca65-primer/demos/compile.sh b/ca65-primer/demos/compile.sh new file mode 100755 index 0000000..5dc0487 --- /dev/null +++ b/ca65-primer/demos/compile.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# +set -o xtrace +ca65 --cpu 6502 --listing --include-dir .. bigdudes.s +ld65 -C ../basic+8k.cfg -o bigdudes.prg ../basic.o bigdudes.o ../VIC-SSS-MMX.o +ca65 --cpu 6502 --listing --include-dir .. hello.s +ld65 -C ../basic+8k.cfg -o hello.prg ../basic.o hello.o ../VIC-SSS-MMX.o +set +o xtrace + +echo -n "Press RETURN: " && read N + +# UNCOMMENT YOUR CHOICE OF EMULATORS ... + +#mess -debug vic20 -ramsize 16k -quik demo.prg +#xvic -memory 8k -autostart demo.prg + +exit + diff --git a/ca65-primer/demos/demo.prg b/ca65-primer/demos/demo.prg new file mode 100755 index 0000000..4bf1355 Binary files /dev/null and b/ca65-primer/demos/demo.prg differ diff --git a/ca65-primer/demos/hello.prg b/ca65-primer/demos/hello.prg new file mode 100755 index 0000000..a8bde96 Binary files /dev/null and b/ca65-primer/demos/hello.prg differ diff --git a/ca65-primer/demos/hello.s b/ca65-primer/demos/hello.s new file mode 100755 index 0000000..0a78b22 --- /dev/null +++ b/ca65-primer/demos/hello.s @@ -0,0 +1,183 @@ +;********************************************************************* +; Simple HELLO demo +; using the VIC Software Sprite Stack +; last updated: 5-May-2009 +; + .include "VIC-SSS-MMX.h" + .segment "CODE" + + JSR SSSINIT + + SEI + LDX #SSSIRQ + STX $0314 + STY $0315 + CLI + + LDA #$02 + STA COLORCODE + LDA #$B1 + LDX #$00 + LDY #$00 + JSR SSSCELL + LDY #$00 + JSR SSSFLIP + + LDA #$06 + STA COLORCODE + LDA #$B2 + LDX #$01 + LDY #$00 + JSR SSSCELL + LDY #$00 + JSR SSSFLIP + + LDX #$07 + LDY #$01 + JSR SSSPLOT + LDA #$98 + JSR SSSPOKE + + LDX #$08 + LDY #$02 + JSR SSSPLOT + LDA #$99 + JSR SSSPOKE + + LDX #$09 + LDY #$03 + JSR SSSPLOT + LDA #$9A + JSR SSSPOKE + +@init: + LDA #%10001100 ; 8x8 floating sprite + LDY #$08 ; all 8 pixels high + JSR SSSCREATE + LDA #$02 ; red + LDX #$40 ; points to + LDY #$80 ; ROM "H" character + JSR SSSANIM + + LDA #%10001100 ; 8x8 floating sprite + LDY #$08 ; all 8 pixels high + JSR SSSCREATE + LDA #$05 ; green + LDX #$28 ; points to + LDY #$80 ; ROM "E" character + JSR SSSANIM + + LDA #%10001100 ; 8x8 floating sprite + LDY #$08 ; all 8 pixels high + JSR SSSCREATE + LDA #$06 ; blue + LDX #$60 ; points to + LDY #$80 ; ROM "L" character + JSR SSSANIM + + LDA #%10001100 ; 8x8 floating sprite + LDY #$08 ; all 8 pixels high + JSR SSSCREATE + LDA #$06 ; blue + LDX #$60 ; points to + LDY #$80 ; ROM "L" character + JSR SSSANIM + + LDA #%10001100 ; 8x8 floating sprite + LDY #$08 ; all 8 pixels high + JSR SSSCREATE + LDA #$04 ; magenta + LDX #$78 ; points to + LDY #$80 ; ROM "O" character + JSR SSSANIM + + LDY #$01 + JSR SSSFLIP +@pos: + LDX #$30 + LDY #$20 + STX R0 + STY R1 + + LDX #$00 + JSR SSSUSE + LDX R0 + LDY R1 + JSR SSSMOVEXY + + LDX #$01 + JSR SSSUSE + LDA R0 + CLC + ADC #$10 + TAX + LDY R1 + JSR SSSMOVEXY + + LDX #$02 + JSR SSSUSE + LDA R0 + CLC + ADC #$20 + TAX + LDY R1 + JSR SSSMOVEXY + + LDX #$03 + JSR SSSUSE + LDA R0 + CLC + ADC #$30 + TAX + LDY R1 + JSR SSSMOVEXY + + LDX #$04 + JSR SSSUSE + LDA R0 + CLC + ADC #$40 + TAX + LDY R1 + JSR SSSMOVEXY + + LDY #$01 + JSR SSSFLIP + LDY #$03 + STY R1 + +@loop: + LDX #$00 + STX R0 + ; +@dy: JSR SSSUSE + LDA SPRITEY,X + BNE @ff + DEC R1 + LDY R1 + CPY #$04 + BCC @ff + LDY #$03 + STY R1 +@ff: TAY + INY + LDA SPRITEX,X + TAX + ;INX + JSR SSSMOVEXY + INC R0 + LDX R0 + CPX #$05 + BNE @dy + + ;LDY R1 + ;JSR SSSFFLIP + LDY #$00 + JSR SSSFLIP + JSR GETIN + CMP #$85 ; got F1? + BEQ @fini + JMP @loop +@fini: + JMP RESET diff --git a/ca65-primer/doc/CREDITS b/ca65-primer/doc/CREDITS new file mode 100755 index 0000000..33d332b --- /dev/null +++ b/ca65-primer/doc/CREDITS @@ -0,0 +1,159 @@ + +Many special thanks go to the guy who started it all: + + John R. Dunning + +Without his great work, there would not be a single freeware C crosscompiler +for the 6502 out there. He built the grounds for this cc65 and some other cc65 +implementations and a lot of his code is still in the current compiler. + + + +More special thanks to: + + * Keith W. Gerdes + + Without his outstanding help, the assembler/compiler wouldn't be, what it + is now. He helped with suggestions, bug reports and even kicked me here + and then, when I started to get too lazy:-) + + * Kevin Ruland + + Kevin did the Apple 2 port and found at least one serious error in the + C library while doing so. + + * Christian Groessler + Mark Keates + Freddy Offenga + David Lloyd + + The team that added support for the Atari 8 bit machines. + Christian Groessler also sent me several fixes for 64 bit machines. + + * Sidney Cadot + + Sidney rewrote the random number generator. + + * Maciej Witkowiak + + Maciej wrote the GEOS support libraries for cc65. + + * Eric Au + + Eric is one of the most active testers, and supplied me with dozens of + bug reports. + + * MicroSystems Development Technologies Inc. located in San Jose, + California payed me for the addition of several assembler features, + which went also into the freeware version. These guys are selling + nice hardware devices like EPROM emulators. If you are developing + hardware or embedded microcontroller applications, you should have + a look at their web site at www.msd.com. + + * Mirco Miranda + + Miroc contributed Makefile additions, docs and patches to compile cc65 + cleanly under OS/2 using the EMX toolkit. + + * Marc 'BlackJack' Rintsch + + Marc wrote and contributed BASIC compatible file I/O routines for the + Commodore machines. + + * Groepaz + + Thanks for several nice samples programs, the NES port, and a lot of other + code. + + * Craig Bruce + + ...for his public domain Swiftlink/Turbo232 drivers which are part + of the cc65 library in modified form. + + * Steve Schmidtke + + Steve contributed the VIC20 port. + + * Michael Klein + + for the Debian support files. + + * Greg King + + reported quite some bugs and helped with lots of code and suggestions. + + * MagerValp + + for sample code regarding the Plus/4 banking, the base for the new C128 + conio library supporting 80 column mode and much more. + + * Piotr Fusik + + for the zlib routines, lots of bug reports, code snippets and + suggestions. + + * Josef Soucek + + Josef contributed the CBM directory routines. + + * Stephen L. Judd + + for his GRLIB code which is the base of the C64 320x200 TGI driver. + + * Stefan A. Haubenthal + + Stefan contributed several code snippets for the C64 and Apple ][. + + * Peter Trauner + + Peter added minimal Supervision support. + + * The Lynx guys: Bastian Schick, who contributed the code from his own, + lynx-only version of cc65, and Karri Kaksonen and Shawn + Jefferson who built on this foundation. + + * Oliver Schmidt ... + + ... for quite some Apple ][ contributions. + + + +Thanks to + + da Blondie + Adam Dunkels + Bill Craig + C. N. Wong + Carsten Strotmann + Chris Ward + Dagan Galarneau + Darrell Krulce + Dennis Lin + Eric Bacher + Gábor Lénárt + Ingo Korb + Jacek Jozwiak + Jaleco + Jari Tuominen + Jesse Beach + Joerg Schwedes + John Weidman + Jonathan Wright + Kevin Schuetz + Mark Nasgowitz + Peter Karlsson + Peter Wendrich + Robert R. Wal + Shawn Jefferson + Stefan Andree + Stephan Lesch + Tim Vanderhoek + Todd Elliott + +for bug reports and suggestions. + + +This list is probably incomplete. So, if you think you should be mentioned +here, but cannot find yourself, please drop me a mail. + + diff --git a/ca65-primer/doc/announce.txt b/ca65-primer/doc/announce.txt new file mode 100755 index 0000000..e0fd59d --- /dev/null +++ b/ca65-primer/doc/announce.txt @@ -0,0 +1,109 @@ + +I'm proud to announce version 2.13.1 of cc65. cc65 is a complete cross +development package for 65(C)02 systems, including a powerful macro assembler, +a C compiler, linker, librarian and several other tools. + +cc65 has C and runtime library support for many of the old 6502 machines, +including + + - the following Commodore machines: + + VIC20 + C16/C116 and Plus/4 + C64 + C128 + CBM 510 (aka P500) + the 600/700 family + newer PET machines (not 2001). + + - the Apple ][ and successors. + + - the Atari 8 bit machines. + + - GEOS for the C64 and C128. + + - the Nintendo Entertainment System (NES). + + - the Supervision console. + + - the Oric Atmos. + + - the Lynx Console. + +The libraries are fairly portable, so creating a version for other 6502s +shouldn't be too much work. + +This version is a bugfix release against 2.13.0. Changes have been made in the +following areas: + + * Added a missing external declaration for ser_load_driver. + + * Fixed an error in the 32K linker config for the Commodore VIC-20. + + * Fixed a problem with bit-fields. + + * Fixed a problem with initialization of local arrays with unspecified size. + + * Function designators are now handled correctly when dereferenced. + + * Corrected a problem with qualifier handling in arrays and structs/unions. + + * Passing structs by value was never possible but is now correctly refused + by the compiler. + + * Ignore unknown preprocessor directives in an #if group that is excluded. + + * Apple ][ target makes use of bit-fields in dirent.h. + + * Documentation improvements. + + +Precompiled binaries are available for the most common platforms. On many +other host platforms, the sources compile out of the box, so you'll able to +create your own binaries. + +For RedHat Enterprise Linux 5, ready made RPM packages are available. These +packages may also work on other rpm-based Linux systems. Please note that +there are separate RPMs for the compiler proper, the docs, and the target +specific libraries. To develop code for one of the target machines, you need +the compiler RPM package *and* one of the target machine packages. Be sure to +download the documentation package if you're new to cc65. + +For windows users, there's a user friendly installer package (the one with the +.exe extension). All subpackages are selectable from within the installer +dialogue. The installer will also setup the necessary environment variables +and start menu entries. It is recommended that Windows users choose this +package instead of the .ZIP files. + +Precompiled binaries for DOS, OS/2 and Windows are available in ZIP archives. +As with the RPM packages, you need the package for the development host system +(Windows or whatever) *and* one or more of the target machine packages, plus +optionally the doc package. + +All packages are available from the MU software FTP server: + + ftp://ftp.musoftware.de/pub/uz/cc65/ + +More information on cc65 can be found on the cc65 web page at + + http://www.cc65.org/ + +There is also a mailing list for discussing cc65 related issues (programming, +suggestions, bugs, ...). See + + http://www.cc65.org/#List + +for information on how to subscribe to this list. + +While I'm the main developer of cc65, it is actually a joint effort. I would +like to thank the target library developers, all the people on the cc65 +mailing list, all those sending suggestions and feedback, and even those +bugging me for a new stable release:-) The CREDITS file is probably +incomplete, but you know who you are. + +Thank you! + + + Uz + + diff --git a/ca65-primer/doc/ca65-1.html b/ca65-primer/doc/ca65-1.html new file mode 100755 index 0000000..7c2ea4e --- /dev/null +++ b/ca65-primer/doc/ca65-1.html @@ -0,0 +1,85 @@ + + + + + ca65 Users Guide: Overview + + + + + +Next +Previous +Contents +
+

1. Overview

+ + +

ca65 is a replacement for the ra65 assembler that was part of the cc65 C +compiler, originally developed by John R. Dunning. I had some problems with +ra65 and the copyright does not permit some things which I wanted to be +possible, so I decided to write a completely new assembler/linker/archiver +suite for the cc65 compiler. ca65 is part of this suite.

+

Some parts of the assembler (code generation and some routines for symbol +table handling) are taken from an older crossassembler named a816 written +by me a long time ago.

+ + +

1.1 Design criteria +

+ + +

Here's a list of the design criteria, that I considered important for the +development:

+

+

    +
  • The assembler must support macros. Macros are not essential, but they +make some things easier, especially when you use the assembler in the +backend of a compiler.
  • +
  • The assembler must support the newer 65C02 and 65816 CPUs. I have been +thinking about a 65816 backend for the C compiler, and even my old +a816 assembler had support for these CPUs, so this wasn't really a +problem.
  • +
  • The assembler must produce relocatable code. This is necessary for the +compiler support, and it is more convenient.
  • +
  • Conditional assembly must be supported. This is a must for bigger +projects written in assembler (like Elite128).
  • +
  • The assembler must support segments, and it must support more than +three segments (this is the count, most other assemblers support). +Having more than one code segments helps developing code for systems +with a divided ROM area (like the C64).
  • +
  • The linker must be able to resolve arbitrary expressions. It should +be able to get things like +
    +
    +        .import S1, S2
    +        .export Special
    +        Special = 2*S1 + S2/7
    +
    +
    + +right.
  • +
  • True lexical nesting for symbols. This is very convenient for larger +assembly projects.
  • +
  • "Cheap" local symbols without lexical nesting for those quick, late +night hacks.
  • +
  • I liked the idea of "options" as Anre Fachats .o65 format has it, so I +introduced the concept into the object file format use by the new cc65 +binutils.
  • +
  • The assembler will be a one pass assembler. There was no real need for +this decision, but I've written several multipass assemblers, and it +started to get boring. A one pass assembler needs much more elaborated +data structures, and because of that it's much more fun:-)
  • +
  • Non-GPLed code that may be used in any project without restrictions or +fear of "GPL infecting" other code.
  • +
+

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-10.html b/ca65-primer/doc/ca65-10.html new file mode 100755 index 0000000..5a639ed --- /dev/null +++ b/ca65-primer/doc/ca65-10.html @@ -0,0 +1,612 @@ + + + + + ca65 Users Guide: Pseudo functions + + + + + +Next +Previous +Contents +
+

10. Pseudo functions

+ + +

Pseudo functions expect their arguments in parenthesis, and they have a result, +either a string or an expression.

+ + +

10.1 .BANKBYTE +

+ + +

The function returns the bank byte (that is, bits 16-23) of its argument. +It works identical to the '^' operator.

+

See: +.HIBYTE, + +.LOBYTE

+ + +

10.2 .BLANK +

+ + +

Builtin function. The function evaluates its argument in braces and yields +"false" if the argument is non blank (there is an argument), and "true" if +there is no argument. The token list that makes up the function argument +may optionally be enclosed in curly braces. This allows the inclusion of +tokens that would otherwise terminate the list (the closing right +parenthesis). The curly braces are not considered part of the list, a list +just consisting of curly braces is considered to be empty.

+

As an example, the .IFBLANK statement may be replaced by

+

+

+
+        .if     .blank({arg})
+  
+
+
+

+ + + +

10.3 .CONCAT +

+ + +

Builtin string function. The function allows to concatenate a list of string +constants separated by commas. The result is a string constant that is the +concatenation of all arguments. This function is most useful in macros and +when used together with the .STRING builtin function. The function may +be used in any case where a string constant is expected.

+

Example:

+

+

+
+        .include        .concat ("myheader", ".", "inc")
+  
+
+
+

+

This is the same as the command

+

+

+
+        .include        "myheader.inc"
+  
+
+
+

+ + +

10.4 .CONST +

+ + +

Builtin function. The function evaluates its argument in braces and +yields "true" if the argument is a constant expression (that is, an +expression that yields a constant value at assembly time) and "false" +otherwise. As an example, the .IFCONST statement may be replaced by

+

+

+
+        .if     .const(a + 3)
+  
+
+
+

+ + +

10.5 .HIBYTE +

+ + +

The function returns the high byte (that is, bits 8-15) of its argument. +It works identical to the '>' operator.

+

See: +.LOBYTE, + +.BANKBYTE

+ + +

10.6 .HIWORD +

+ + +

The function returns the high word (that is, bits 16-31) of its argument.

+

See: +.LOWORD

+ + +

10.7 .IDENT +

+ + +

The function expects a string as its argument, and converts this argument +into an identifier. If the string starts with the current +.LOCALCHAR, it will be converted into a cheap local +identifier, otherwise it will be converted into a normal identifier.

+

Example:

+

+

+
+        .macro  makelabel       arg1, arg2
+                .ident (.concat (arg1, arg2)):
+        .endmacro
+
+                makelabel       "foo", "bar"
+
+                .word           foobar          ; Valid label
+  
+
+
+

+ + +

10.8 .LEFT +

+ + +

Builtin function. Extracts the left part of a given token list.

+

Syntax:

+

+

+
+        .LEFT (<int expr>, <token list>)
+  
+
+
+

+

The first integer expression gives the number of tokens to extract from +the token list. The second argument is the token list itself. The token +list may optionally be enclosed into curly braces. This allows the +inclusion of tokens that would otherwise terminate the list (the closing +right paren in the given case).

+

Example:

+

To check in a macro if the given argument has a '#' as first token +(immediate addressing mode), use something like this:

+

+

+
+        .macro  ldax    arg
+                ...
+                .if (.match (.left (1, {arg}), #))
+
+                ; ldax called with immediate operand
+                ...
+
+                .endif
+                ...
+        .endmacro
+  
+
+
+

+

See also the +.MID and +.RIGHT builtin functions.

+ + +

10.9 .LOBYTE +

+ + +

The function returns the low byte (that is, bits 0-7) of its argument. +It works identical to the '<' operator.

+

See: +.HIBYTE, + +.BANKBYTE

+ + +

10.10 .LOWORD +

+ + +

The function returns the low word (that is, bits 0-15) of its argument.

+

See: +.HIWORD

+ + +

10.11 .MATCH +

+ + +

Builtin function. Matches two token lists against each other. This is +most useful within macros, since macros are not stored as strings, but +as lists of tokens.

+

The syntax is

+

+

+
+        .MATCH(<token list #1>, <token list #2>)
+  
+
+
+

+

Both token list may contain arbitrary tokens with the exception of the +terminator token (comma resp. right parenthesis) and

+

+

    +
  • end-of-line
  • +
  • end-of-file
  • +
+

+

The token lists may optionally be enclosed into curly braces. This allows +the inclusion of tokens that would otherwise terminate the list (the closing +right paren in the given case). Often a macro parameter is used for any of +the token lists.

+

Please note that the function does only compare tokens, not token +attributes. So any number is equal to any other number, regardless of the +actual value. The same is true for strings. If you need to compare tokens +and token attributes, use the +.XMATCH function.

+

Example:

+

Assume the macro ASR, that will shift right the accumulator by one, +while honoring the sign bit. The builtin processor instructions will allow +an optional "A" for accu addressing for instructions like ROL and +ROR. We will use the +.MATCH function +to check for this and print and error for invalid calls.

+

+

+
+        .macro  asr     arg
+
+                .if (.not .blank(arg)) .and (.not .match ({arg}, a))
+                .error "Syntax error"
+                .endif
+
+                cmp     #$80            ; Bit 7 into carry
+                lsr     a               ; Shift carry into bit 7
+
+        .endmacro
+  
+
+
+

+

The macro will only accept no arguments, or one argument that must be the +reserved keyword "A".

+

See: +.XMATCH

+ + +

10.12 .MID +

+ + +

Builtin function. Takes a starting index, a count and a token list as +arguments. Will return part of the token list.

+

Syntax:

+

+

+
+        .MID (<int expr>, <int expr>, <token list>)
+  
+
+
+

+

The first integer expression gives the starting token in the list (the first +token has index 0). The second integer expression gives the number of tokens +to extract from the token list. The third argument is the token list itself. +The token list may optionally be enclosed into curly braces. This allows the +inclusion of tokens that would otherwise terminate the list (the closing +right paren in the given case).

+

Example:

+

To check in a macro if the given argument has a '#' as first token +(immediate addressing mode), use something like this:

+

+

+
+        .macro  ldax    arg
+                ...
+                .if (.match (.mid (0, 1, {arg}), #))
+
+                ; ldax called with immediate operand
+                ...
+
+                .endif
+                ...
+        .endmacro
+  
+
+
+

+

See also the +.LEFT and +.RIGHT builtin functions.

+ + +

10.13 .REF, .REFERENCED +

+ + +

Builtin function. The function expects an identifier as argument in braces. +The argument is evaluated, and the function yields "true" if the identifier +is a symbol that has already been referenced somewhere in the source file up +to the current position. Otherwise the function yields false. As an example, +the +.IFREF statement may be replaced by

+

+

+
+        .if     .referenced(a)
+  
+
+
+

+

See: +.DEFINED

+ + +

10.14 .RIGHT +

+ + +

Builtin function. Extracts the right part of a given token list.

+

Syntax:

+

+

+
+        .RIGHT (<int expr>, <token list>)
+  
+
+
+

+

The first integer expression gives the number of tokens to extract from the +token list. The second argument is the token list itself. The token list +may optionally be enclosed into curly braces. This allows the inclusion of +tokens that would otherwise terminate the list (the closing right paren in +the given case).

+

See also the +.LEFT and +.MID builtin functions.

+ + +

10.15 .SIZEOF +

+ + +

.SIZEOF is a pseudo function that returns the size of its argument. The +argument can be a struct/union, a struct member, a procedure, or a label. In +case of a procedure or label, its size is defined by the amount of data +placed in the segment where the label is relative to. If a line of code +switches segments (for example in a macro) data placed in other segments +does not count for the size.

+

Please note that a symbol or scope must exist, before it is used together with +.SIZEOF (this may get relaxed later, but will always be true for scopes). +A scope has preference over a symbol with the same name, so if the last part +of a name represents both, a scope and a symbol, the scope is chosen over the +symbol.

+

After the following code:

+

+

+
+        .struct Point                   ; Struct size = 4
+                xcoord  .word
+                xcoord  .word
+        .endstruct
+
+        P:      .tag    Point           ; Declare a point
+        @P:     .tag    Point           ; Declare another point
+
+        .code
+        .proc   Code
+                nop
+                .proc   Inner
+                        nop
+                .endproc
+                nop
+        .endproc
+
+        .proc   Data
+        .data                           ; Segment switch!!!
+                .res    4
+        .endproc
+  
+
+
+

+

+

+
.sizeof(Point)
+

will have the value 4, because this is the size of struct Point.

+ +
.sizeof(Point::xcoord)
+

will have the value 2, because this is the size of the member xcoord +in struct Point.

+ +
.sizeof(P)
+

will have the value 4, this is the size of the data declared on the same +source line as the label P, which is in the same segment that P +is relative to.

+ +
.sizeof(@P)
+

will have the value 4, see above. The example demonstrates that .SIZEOF +does also work for cheap local symbols.

+ +
.sizeof(Code)
+

will have the value 3, since this is amount of data emitted into the code +segment, the segment that was active when Code was entered. Note that +this value includes the amount of data emitted in child scopes (in this +case Code::Inner).

+ +
.sizeof(Code::Inner)
+

will have the value 1 as expected.

+ +
.sizeof(Data)
+

will have the value 0. Data is emitted within the scope Data, but since +the segment is switched after entry, this data is emitted into another +segment.

+
+

+ + +

10.16 .STRAT +

+ + +

Builtin function. The function accepts a string and an index as +arguments and returns the value of the character at the given position +as an integer value. The index is zero based.

+

Example:

+

+

+
+        .macro  M       Arg
+                ; Check if the argument string starts with '#'
+                .if (.strat (Arg, 0) = '#')
+                ...
+                .endif
+        .endmacro
+  
+
+
+

+ + +

10.17 .SPRINTF +

+ + +

Builtin function. It expects a format string as first argument. The number +and type of the following arguments depend on the format string. The format +string is similar to the one of the C printf function. Missing things +are: Length modifiers, variable width.

+

The result of the function is a string.

+

Example:

+

+

+
+        num     = 3
+
+        ; Generate an identifier:
+        .ident (.sprintf ("%s%03d", "label", num)):
+  
+
+
+

+ + +

10.18 .STRING +

+ + +

Builtin function. The function accepts an argument in braces and converts +this argument into a string constant. The argument may be an identifier, or +a constant numeric value.

+

Since you can use a string in the first place, the use of the function may +not be obvious. However, it is useful in macros, or more complex setups.

+

Example:

+

+

+
+        ; Emulate other assemblers:
+        .macro  section name
+                .segment        .string(name)
+        .endmacro
+  
+
+
+

+ + +

10.19 .STRLEN +

+ + +

Builtin function. The function accepts a string argument in braces and +evaluates to the length of the string.

+

Example:

+

The following macro encodes a string as a pascal style string with +a leading length byte.

+

+

+
+        .macro  PString Arg
+                .byte   .strlen(Arg), Arg
+        .endmacro
+  
+
+
+

+ + +

10.20 .TCOUNT +

+ + +

Builtin function. The function accepts a token list in braces. The function +result is the number of tokens given as argument. The token list may +optionally be enclosed into curly braces which are not considered part of +the list and not counted. Enclosement in curly braces allows the inclusion +of tokens that would otherwise terminate the list (the closing right paren +in the given case).

+

Example:

+

The ldax macro accepts the '#' token to denote immediate addressing (as +with the normal 6502 instructions). To translate it into two separate 8 bit +load instructions, the '#' token has to get stripped from the argument:

+

+

+
+        .macro  ldax    arg
+                .if (.match (.mid (0, 1, {arg}), #))
+                ; ldax called with immediate operand
+                lda     #<(.right (.tcount ({arg})-1, {arg}))
+                ldx     #>(.right (.tcount ({arg})-1, {arg}))
+                .else
+                ...
+                .endif
+        .endmacro
+  
+
+
+

+ + +

10.21 .XMATCH +

+ + +

Builtin function. Matches two token lists against each other. This is +most useful within macros, since macros are not stored as strings, but +as lists of tokens.

+

The syntax is

+

+

+
+        .XMATCH(<token list #1>, <token list #2>)
+  
+
+
+

+

Both token list may contain arbitrary tokens with the exception of the +terminator token (comma resp. right parenthesis) and

+

+

    +
  • end-of-line
  • +
  • end-of-file
  • +
+

+

The token lists may optionally be enclosed into curly braces. This allows +the inclusion of tokens that would otherwise terminate the list (the closing +right paren in the given case). Often a macro parameter is used for any of +the token lists.

+

The function compares tokens and token values. If you need a function +that just compares the type of tokens, have a look at the +.MATCH function.

+

See: +.MATCH

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-11.html b/ca65-primer/doc/ca65-11.html new file mode 100755 index 0000000..3c89fd1 --- /dev/null +++ b/ca65-primer/doc/ca65-11.html @@ -0,0 +1,2199 @@ + + + + + ca65 Users Guide: Control commands + + + + + +Next +Previous +Contents +
+

11. Control commands

+ + +

Here's a list of all control commands and a description, what they do:

+ + +

11.1 .A16 +

+ + +

Valid only in 65816 mode. Switch the accumulator to 16 bit.

+

Note: This command will not emit any code, it will tell the assembler to +create 16 bit operands for immediate accumulator addressing mode.

+

See also: +.SMART

+ + +

11.2 .A8 +

+ + +

Valid only in 65816 mode. Switch the accumulator to 8 bit.

+

Note: This command will not emit any code, it will tell the assembler to +create 8 bit operands for immediate accu addressing mode.

+

See also: +.SMART

+ + +

11.3 .ADDR +

+ + +

Define word sized data. In 6502 mode, this is an alias for .WORD and +may be used for better readability if the data words are address values. In +65816 mode, the address is forced to be 16 bit wide to fit into the current +segment. See also +.FARADDR. The command +must be followed by a sequence of (not necessarily constant) expressions.

+

Example:

+

+

+
+        .addr   $0D00, $AF13, _Clear
+  
+
+
+

+

See: +.FARADDR, +.WORD

+ + +

11.4 .ALIGN +

+ + +

Align data to a given boundary. The command expects a constant integer +argument that must be a power of two, plus an optional second argument +in byte range. If there is a second argument, it is used as fill value, +otherwise the value defined in the linker configuration file is used +(the default for this value is zero).

+

Since alignment depends on the base address of the module, you must +give the same (or a greater) alignment for the segment when linking. +The linker will give you a warning, if you don't do that.

+

Example:

+

+

+
+        .align  256
+  
+
+
+

+ + +

11.5 .ASCIIZ +

+ + +

Define a string with a trailing zero.

+

Example:

+

+

+
+        Msg:    .asciiz "Hello world"
+  
+
+
+

+

This will put the string "Hello world" followed by a binary zero into +the current segment. There may be more strings separated by commas, but +the binary zero is only appended once (after the last one).

+ + +

11.6 .ASSERT +

+ + +

Add an assertion. The command is followed by an expression, an action +specifier, and an optional message that is output in case the assertion +fails. If no message was given, the string "Assertion failed" is used. The +action specifier may be one of warning or error. The assertion is +evaluated by the assembler if possible, and also passed to the linker in the +object file (if one is generated). The linker will then evaluate the +expression when segment placement has been done.

+

Example:

+

+

+
+        .assert         * = $8000, error, "Code not at $8000"
+  
+
+
+

+

The example assertion will check that the current location is at $8000, +when the output file is written, and abort with an error if this is not +the case. More complex expressions are possible. The action specifier +warning outputs a warning, while the error specifier outputs +an error message. In the latter case, generation of the output file is +suppressed in both the assembler and linker.

+ + +

11.7 .AUTOIMPORT +

+ + +

Is followed by a plus or a minus character. When switched on (using a ++), undefined symbols are automatically marked as import instead of +giving errors. When switched off (which is the default so this does not +make much sense), this does not happen and an error message is +displayed. The state of the autoimport flag is evaluated when the +complete source was translated, before outputting actual code, so it is +not possible to switch this feature on or off for separate sections +of code. The last setting is used for all symbols.

+

You should probably not use this switch because it delays error +messages about undefined symbols until the link stage. The cc65 +compiler (which is supposed to produce correct assembler code in all +circumstances, something which is not true for most assembler +programmers) will insert this command to avoid importing each and every +routine from the runtime library.

+

Example:

+

+

+
+        .autoimport     +       ; Switch on auto import
+  
+
+
+

+ +

11.8 .BANKBYTES +

+ + +

Define byte sized data by extracting only the bank byte (that is, bits 16-23) from +each expression. This is equivalent to +.BYTE with +the operator '^' prepended to each expression in its list.

+

Example:

+

+

+
+        .define MyTable TableItem0, TableItem1, TableItem2, TableItem3
+
+        TableLookupLo:   .lobytes   MyTable
+        TableLookupHi:   .hibytes   MyTable
+        TableLookupBank: .bankbytes MyTable
+  
+
+
+

+

which is equivalent to

+

+

+
+        TableLookupLo:   .byte <TableItem0, <TableItem1, <TableItem2, <TableItem3
+        TableLookupHi:   .byte >TableItem0, >TableItem1, >TableItem2, >TableItem3
+        TableLookupBank: .byte ^TableItem0, ^TableItem1, ^TableItem2, ^TableItem3
+  
+
+
+

+

See also: +.BYTE, + +.HIBYTES, + +.LOBYTES

+ + +

11.9 .BSS +

+ + +

Switch to the BSS segment. The name of the BSS segment is always "BSS", +so this is a shortcut for

+

+

+
+        .segment  "BSS"
+  
+
+
+

+

See also the +.SEGMENT command.

+ + +

11.10 .BYT, .BYTE +

+ + +

Define byte sized data. Must be followed by a sequence of (byte ranged) +expressions or strings.

+

Example:

+

+

+
+        .byte   "Hello "
+        .byt    "world", $0D, $00
+  
+
+
+

+ + +

11.11 .CASE +

+ + +

Switch on or off case sensitivity on identifiers. The default is off +(that is, identifiers are case sensitive), but may be changed by the +-i switch on the command line. +The command must be followed by a '+' or '-' character to switch the +option on or off respectively.

+

Example:

+

+

+
+        .case   -               ; Identifiers are not case sensitive
+  
+
+
+

+ + +

11.12 .CHARMAP +

+ + +

Apply a custom mapping for characters. The command is followed by two +numbers in the range 1..255. The first one is the index of the source +character, the second one is the mapping. The mapping applies to all +character and string constants when they generate output, and overrides +a mapping table specified with the +-t +command line switch.

+

Example:

+

+

+
+        .charmap        $41, $61        ; Map 'A' to 'a'
+  
+
+
+

+ + +

11.13 .CODE +

+ + +

Switch to the CODE segment. The name of the CODE segment is always +"CODE", so this is a shortcut for

+

+

+
+        .segment  "CODE"
+  
+
+
+

+

See also the +.SEGMENT command.

+ + +

11.14 .CONDES +

+ + +

Export a symbol and mark it in a special way. The linker is able to build +tables of all such symbols. This may be used to automatically create a list +of functions needed to initialize linked library modules.

+

Note: The linker has a feature to build a table of marked routines, but it +is your code that must call these routines, so just declaring a symbol with +.CONDES does nothing by itself.

+

All symbols are exported as an absolute (16 bit) symbol. You don't need to +use an additional +.EXPORT statement, this +is implied by .CONDES.

+

.CONDES is followed by the type, which may be constructor, +destructor or a numeric value between 0 and 6 (where 0 is the same as +specifying constructor and 1 is equal to specifying destructor). +The +.CONSTRUCTOR, +.DESTRUCTOR and +.INTERRUPTOR commands are actually shortcuts for .CONDES +with a type of constructor resp. destructor or interruptor.

+

After the type, an optional priority may be specified. Higher numeric values +mean higher priority. If no priority is given, the default priority of 7 is +used. Be careful when assigning priorities to your own module constructors +so they won't interfere with the ones in the cc65 library.

+

Example:

+

+

+
+        .condes         ModuleInit, constructor
+        .condes         ModInit, 0, 16
+  
+
+
+

+

See the +.CONSTRUCTOR, +.DESTRUCTOR and +.INTERRUPTOR commands and the separate section +Module constructors/destructors explaining the feature in more +detail.

+ + +

11.15 .CONSTRUCTOR +

+ + +

Export a symbol and mark it as a module constructor. This may be used +together with the linker to build a table of constructor subroutines that +are called by the startup code.

+

Note: The linker has a feature to build a table of marked routines, but it +is your code that must call these routines, so just declaring a symbol as +constructor does nothing by itself.

+

A constructor is always exported as an absolute (16 bit) symbol. You don't +need to use an additional .export statement, this is implied by +.constructor. It may have an optional priority that is separated by a +comma. Higher numeric values mean a higher priority. If no priority is +given, the default priority of 7 is used. Be careful when assigning +priorities to your own module constructors so they won't interfere with the +ones in the cc65 library.

+

Example:

+

+

+
+        .constructor    ModuleInit
+        .constructor    ModInit, 16
+  
+
+
+

+

See the +.CONDES and +.DESTRUCTOR commands and the separate section +Module constructors/destructors explaining the +feature in more detail.

+ + +

11.16 .DATA +

+ + +

Switch to the DATA segment. The name of the DATA segment is always +"DATA", so this is a shortcut for

+

+

+
+        .segment  "DATA"
+  
+
+
+

+

See also the +.SEGMENT command.

+ + +

11.17 .DBYT +

+ + +

Define word sized data with the hi and lo bytes swapped (use .WORD to +create word sized data in native 65XX format). Must be followed by a +sequence of (word ranged) expressions.

+

Example:

+

+

+
+        .dbyt   $1234, $4512
+  
+
+
+

+

This will emit the bytes

+

+

+
+        $12 $34 $45 $12
+  
+
+
+

+

into the current segment in that order.

+ + +

11.18 .DEBUGINFO +

+ + +

Switch on or off debug info generation. The default is off (that is, +the object file will not contain debug infos), but may be changed by the +-g switch on the command line. +The command must be followed by a '+' or '-' character to switch the +option on or off respectively.

+

Example:

+

+

+
+        .debuginfo      +       ; Generate debug info
+  
+
+
+

+ + +

11.19 .DEFINE +

+ + +

Start a define style macro definition. The command is followed by an +identifier (the macro name) and optionally by a list of formal arguments +in braces. +See section +Macros.

+ + +

11.20 .DEF, .DEFINED +

+ + +

Builtin function. The function expects an identifier as argument in braces. +The argument is evaluated, and the function yields "true" if the identifier +is a symbol that is already defined somewhere in the source file up to the +current position. Otherwise the function yields false. As an example, the + +.IFDEF statement may be replaced by

+

+

+
+        .if     .defined(a)
+  
+
+
+

+ + +

11.21 .DESTRUCTOR +

+ + +

Export a symbol and mark it as a module destructor. This may be used +together with the linker to build a table of destructor subroutines that +are called by the startup code.

+

Note: The linker has a feature to build a table of marked routines, but it +is your code that must call these routines, so just declaring a symbol as +constructor does nothing by itself.

+

A destructor is always exported as an absolute (16 bit) symbol. You don't +need to use an additional .export statement, this is implied by +.destructor. It may have an optional priority that is separated by a +comma. Higher numerical values mean a higher priority. If no priority is +given, the default priority of 7 is used. Be careful when assigning +priorities to your own module destructors so they won't interfere with the +ones in the cc65 library.

+

Example:

+

+

+
+        .destructor     ModuleDone
+        .destructor     ModDone, 16
+  
+
+
+

+

See the +.CONDES and +.CONSTRUCTOR commands and the separate +section +Module constructors/destructors explaining +the feature in more detail.

+ + +

11.22 .DWORD +

+ + +

Define dword sized data (4 bytes) Must be followed by a sequence of +expressions.

+

Example:

+

+

+
+        .dword  $12344512, $12FA489
+  
+
+
+

+ + +

11.23 .ELSE +

+ + +

Conditional assembly: Reverse the current condition.

+ + +

11.24 .ELSEIF +

+ + +

Conditional assembly: Reverse current condition and test a new one.

+ + +

11.25 .END +

+ + +

Forced end of assembly. Assembly stops at this point, even if the command +is read from an include file.

+ + +

11.26 .ENDENUM +

+ + +

End a +.ENUM declaration.

+ + +

11.27 .ENDIF +

+ + +

Conditional assembly: Close a +.IF... or + +.ELSE branch.

+ + +

11.28 .ENDMAC, .ENDMACRO +

+ + +

End of macro definition (see section +Macros).

+ + +

11.29 .ENDPROC +

+ + +

End of local lexical level (see +.PROC).

+ + +

11.30 .ENDREP, .ENDREPEAT +

+ + +

End a +.REPEAT block.

+ + +

11.31 .ENDSCOPE +

+ + +

End of local lexical level (see +.SCOPE).

+ + +

11.32 .ENDSTRUCT +

+ + +

Ends a struct definition. See the +.STRUCT +command and the separate section named +"Structs and unions".

+ + +

11.33 .ENUM +

+ + +

Start an enumeration. This directive is very similar to the C enum +keyword. If a name is given, a new scope is created for the enumeration, +otherwise the enumeration members are placed in the enclosing scope.

+

In the enumeration body, symbols are declared. The first symbol has a value +of zero, and each following symbol will get the value of the preceding plus +one. This behaviour may be overridden by an explicit assignment. Two symbols +may have the same value.

+

Example:

+

+

+
+        .enum   errorcodes
+                no_error
+                file_error
+                parse_error
+        .endenum
+  
+
+
+

+

Above example will create a new scope named errorcodes with three +symbols in it that get the values 0, 1 and 2 respectively. Another way +to write this would have been:

+

+

+
+        .scope  errorcodes
+                no_error        = 0
+                file_error      = 1
+                parse_error     = 2
+        .endscope
+  
+
+
+

+

Please note that explicit scoping must be used to access the identifiers:

+

+

+
+        .word   errorcodes::no_error
+  
+
+
+

+

A more complex example:

+

+

+
+        .enum
+                EUNKNOWN        = -1
+                EOK
+                EFILE
+                EBUSY
+                EAGAIN
+                EWOULDBLOCK     = EAGAIN
+        .endenum
+  
+
+
+

+

In this example, the enumeration does not have a name, which means that the +members will be visible in the enclosing scope and can be used in this scope +without explicit scoping. The first member (EUNKNOWN) has the value -1. +The value for the following members is incremented by one, so EOK would +be zero and so on. EWOULDBLOCK is an alias for EGAIN, so it has an +override for the value using an already defined symbol.

+ + +

11.34 .ERROR +

+ + +

Force an assembly error. The assembler will output an error message +preceded by "User error" and will not produce an object file.

+

This command may be used to check for initial conditions that must be +set before assembling a source file.

+

Example:

+

+

+
+        .if     foo = 1
+        ...
+        .elseif bar = 1
+        ...
+        .else
+        .error  "Must define foo or bar!"
+        .endif
+  
+
+
+

+

See also the +.WARNING and +.OUT directives.

+ + +

11.35 .EXITMAC, .EXITMACRO +

+ + +

Abort a macro expansion immediately. This command is often useful in +recursive macros. See separate section +Macros.

+ + +

11.36 .EXPORT +

+ + +

Make symbols accessible from other modules. Must be followed by a comma +separated list of symbols to export, with each one optionally followed by an +address specification and (also optional) an assignment. Using an additional +assignment in the export statement allows to define and export a symbol in +one statement. The default is to export the symbol with the address size it +actually has. The assembler will issue a warning, if the symbol is exported +with an address size smaller than the actual address size.

+

Examples:

+

+

+
+        .export foo
+        .export bar: far
+        .export foobar: far = foo * bar
+        .export baz := foobar, zap: far = baz - bar
+  
+
+
+

+

As with constant definitions, using := instead of = marks the +symbols as a label.

+

See: +.EXPORTZP

+ + +

11.37 .EXPORTZP +

+ + +

Make symbols accessible from other modules. Must be followed by a comma +separated list of symbols to export. The exported symbols are explicitly +marked as zero page symbols. An assignment may be included in the +.EXPORTZP statement. This allows to define and export a symbol in one +statement.

+

Examples:

+

+

+
+        .exportzp  foo, bar
+        .exportzp  baz := $02
+  
+
+
+

+

See: +.EXPORT

+ + +

11.38 .FARADDR +

+ + +

Define far (24 bit) address data. The command must be followed by a +sequence of (not necessarily constant) expressions.

+

Example:

+

+

+
+        .faraddr        DrawCircle, DrawRectangle, DrawHexagon
+  
+
+
+

+

See: +.ADDR

+ + +

11.39 .FEATURE +

+ + +

This directive may be used to enable one or more compatibility features +of the assembler. While the use of .FEATURE should be avoided when +possible, it may be useful when porting sources written for other +assemblers. There is no way to switch a feature off, once you have +enabled it, so using

+

+

+
+        .FEATURE        xxx
+  
+
+
+

+

will enable the feature until end of assembly is reached.

+

The following features are available:

+

+

+ +
at_in_identifiers +
+

Accept the at character (`@') as a valid character in identifiers. The +at character is not allowed to start an identifier, even with this +feature enabled.

+ +
c_comments
+

Allow C like comments using /* and */ as left and right +comment terminators. Note that C comments may not be nested. There's also a +pitfall when using C like comments: All statements must be terminated by +"end-of-line". Using C like comments, it is possible to hide the newline, +which results in error messages. See the following non working example:

+

+

+
+        lda     #$00  /* This comment hides the newline
+*/      sta     $82
+    
+
+
+

+ +
dollar_in_identifiers +
+

Accept the dollar sign (`$') as a valid character in identifiers. The +dollar character is not allowed to start an identifier, even with this +feature enabled.

+ +
dollar_is_pc
+

The dollar sign may be used as an alias for the star (`*'), which +gives the value of the current PC in expressions. +Note: Assignment to the pseudo variable is not allowed.

+ +
labels_without_colons
+

Allow labels without a trailing colon. These labels are only accepted, +if they start at the beginning of a line (no leading white space).

+ +
leading_dot_in_identifiers +
+

Accept the dot (`.') as the first character of an identifier. This may be +used for example to create macro names that start with a dot emulating +control directives of other assemblers. Note however, that none of the +reserved keywords built into the assembler, that starts with a dot, may be +overridden. When using this feature, you may also get into trouble if +later versions of the assembler define new keywords starting with a dot.

+ +
loose_char_term
+

Accept single quotes as well as double quotes as terminators for char +constants.

+ +
loose_string_term
+

Accept single quotes as well as double quotes as terminators for string +constants.

+ +
missing_char_term
+

Accept single quoted character constants where the terminating quote is +missing. +

+
+        lda     #'a
+    
+
+
+ +Note: This does not work in conjunction with .FEATURE +loose_string_term, since in this case the input would be ambiguous.

+ +
org_per_seg +
+

This feature makes relocatable/absolute mode local to the current segment. +Using +.ORG when org_per_seg is in +effect will only enable absolute mode for the current segment. Dito for + +.RELOC.

+ +
pc_assignment
+

Allow assignments to the PC symbol (`*' or `$' if dollar_is_pc +is enabled). Such an assignment is handled identical to the +.ORG command (which is usually not needed, so just +removing the lines with the assignments may also be an option when porting +code written for older assemblers).

+ +
ubiquitous_idents
+

Allow the use of instructions names as names for macros and symbols. This +makes it possible to "overload" instructions by defining a macro with the +same name. This does also make it possible to introduce hard to find errors +in your code, so be careful!

+ +
+

+

It is also possible to specify features on the command line using the + +--feature command line option. +This is useful when translating sources written for older assemblers, when +you don't want to change the source code.

+

As an example, to translate sources written for Andre Fachats xa65 +assembler, the features

+

+

+        labels_without_colons, pc_assignment, loose_char_term
+  
+
+

+

may be helpful. They do not make ca65 completely compatible, so you may not +be able to translate the sources without changes, even when enabling these +features. However, I have found several sources that translate without +problems when enabling these features on the command line.

+ + +

11.40 .FILEOPT, .FOPT +

+ + +

Insert an option string into the object file. There are two forms of +this command, one specifies the option by a keyword, the second +specifies it as a number. Since usage of the second one needs knowledge +of the internal encoding, its use is not recommended and I will only +describe the first form here.

+

The command is followed by one of the keywords

+

+

+
+        author
+        comment
+        compiler
+  
+
+
+

+

a comma and a string. The option is written into the object file +together with the string value. This is currently unidirectional and +there is no way to actually use these options once they are in the +object file.

+

Examples:

+

+

+
+        .fileopt        comment, "Code stolen from my brother"
+        .fileopt        compiler, "BASIC 2.0"
+        .fopt           author, "J. R. User"
+  
+
+
+

+ + +

11.41 .FORCEIMPORT +

+ + +

Import an absolute symbol from another module. The command is followed by a +comma separated list of symbols to import. The command is similar to +.IMPORT, but the import reference is always +written to the generated object file, even if the symbol is never referenced +( +.IMPORT will not generate import +references for unused symbols).

+

Example:

+

+

+
+        .forceimport    needthisone, needthistoo
+  
+
+
+

+

See: +.IMPORT

+ + +

11.42 .GLOBAL +

+ + +

Declare symbols as global. Must be followed by a comma separated list of +symbols to declare. Symbols from the list, that are defined somewhere in the +source, are exported, all others are imported. Additional +.IMPORT or +.EXPORT commands for the same symbol are allowed.

+

Example:

+

+

+
+        .global foo, bar
+  
+
+
+

+ + +

11.43 .GLOBALZP +

+ + +

Declare symbols as global. Must be followed by a comma separated list of +symbols to declare. Symbols from the list, that are defined somewhere in the +source, are exported, all others are imported. Additional +.IMPORTZP or +.EXPORTZP commands for the same symbol are allowed. The symbols +in the list are explicitly marked as zero page symbols.

+

Example:

+

+

+
+        .globalzp foo, bar
+  
+
+
+

+ +

11.44 .HIBYTES +

+ + +

Define byte sized data by extracting only the high byte (that is, bits 8-15) from +each expression. This is equivalent to +.BYTE with +the operator '>' prepended to each expression in its list.

+

Example:

+

+

+
+        .lobytes         $1234, $2345, $3456, $4567
+        .hibytes         $fedc, $edcb, $dcba, $cba9
+  
+
+
+

+

which is equivalent to

+

+

+
+        .byte            $34, $45, $56, $67
+        .byte            $fe, $ed, $dc, $cb
+  
+
+
+

+

Example:

+

+

+
+        .define MyTable TableItem0, TableItem1, TableItem2, TableItem3
+
+        TableLookupLo:   .lobytes MyTable
+        TableLookupHi:   .hibytes MyTable
+  
+
+
+

+

which is equivalent to

+

+

+
+        TableLookupLo:   .byte <TableItem0, <TableItem1, <TableItem2, <TableItem3
+        TableLookupHi:   .byte >TableItem0, >TableItem1, >TableItem2, >TableItem3
+  
+
+
+

+

See also: +.BYTE, + +.LOBYTES, + +.BANKBYTES

+ + +

11.45 .I16 +

+ + +

Valid only in 65816 mode. Switch the index registers to 16 bit.

+

Note: This command will not emit any code, it will tell the assembler to +create 16 bit operands for immediate operands.

+

See also the +.I8 and +.SMART commands.

+ + +

11.46 .I8 +

+ + +

Valid only in 65816 mode. Switch the index registers to 8 bit.

+

Note: This command will not emit any code, it will tell the assembler to +create 8 bit operands for immediate operands.

+

See also the +.I16 and +.SMART commands.

+ + +

11.47 .IF +

+ + +

Conditional assembly: Evaluate an expression and switch assembler output +on or off depending on the expression. The expression must be a constant +expression, that is, all operands must be defined.

+

A expression value of zero evaluates to FALSE, any other value evaluates +to TRUE.

+ + +

11.48 .IFBLANK +

+ + +

Conditional assembly: Check if there are any remaining tokens in this line, +and evaluate to FALSE if this is the case, and to TRUE otherwise. If the +condition is not true, further lines are not assembled until an +.ESLE, +.ELSEIF or + +.ENDIF directive.

+

This command is often used to check if a macro parameter was given. Since an +empty macro parameter will evaluate to nothing, the condition will evaluate +to FALSE if an empty parameter was given.

+

Example:

+

+

+
+        .macro     arg1, arg2
+        .ifblank   arg2
+                   lda     #arg1
+        .else
+                   lda     #arg2
+        .endif
+        .endmacro
+  
+
+
+

+

See also: +.BLANK

+ + +

11.49 .IFCONST +

+ + +

Conditional assembly: Evaluate an expression and switch assembler output +on or off depending on the constness of the expression.

+

A const expression evaluates to to TRUE, a non const expression (one +containing an imported or currently undefined symbol) evaluates to +FALSE.

+

See also: +.CONST

+ + +

11.50 .IFDEF +

+ + +

Conditional assembly: Check if a symbol is defined. Must be followed by +a symbol name. The condition is true if the the given symbol is already +defined, and false otherwise.

+

See also: +.DEFINED

+ + +

11.51 .IFNBLANK +

+ + +

Conditional assembly: Check if there are any remaining tokens in this line, +and evaluate to TRUE if this is the case, and to FALSE otherwise. If the +condition is not true, further lines are not assembled until an +.ELSE, +.ELSEIF or + +.ENDIF directive.

+

This command is often used to check if a macro parameter was given. +Since an empty macro parameter will evaluate to nothing, the condition +will evaluate to FALSE if an empty parameter was given.

+

Example:

+

+

+
+        .macro     arg1, arg2
+                   lda     #arg1
+        .ifnblank  arg2
+                   lda     #arg2
+        .endif
+        .endmacro
+  
+
+
+

+

See also: +.BLANK

+ + +

11.52 .IFNDEF +

+ + +

Conditional assembly: Check if a symbol is defined. Must be followed by +a symbol name. The condition is true if the the given symbol is not +defined, and false otherwise.

+

See also: +.DEFINED

+ + +

11.53 .IFNREF +

+ + +

Conditional assembly: Check if a symbol is referenced. Must be followed +by a symbol name. The condition is true if if the the given symbol was +not referenced before, and false otherwise.

+

See also: +.REFERENCED

+ + +

11.54 .IFP02 +

+ + +

Conditional assembly: Check if the assembler is currently in 6502 mode +(see +.P02 command).

+ + +

11.55 .IFP816 +

+ + +

Conditional assembly: Check if the assembler is currently in 65816 mode +(see +.P816 command).

+ + +

11.56 .IFPC02 +

+ + +

Conditional assembly: Check if the assembler is currently in 65C02 mode +(see +.PC02 command).

+ + +

11.57 .IFPSC02 +

+ + +

Conditional assembly: Check if the assembler is currently in 65SC02 mode +(see +.PSC02 command).

+ + +

11.58 .IFREF +

+ + +

Conditional assembly: Check if a symbol is referenced. Must be followed +by a symbol name. The condition is true if if the the given symbol was +referenced before, and false otherwise.

+

This command may be used to build subroutine libraries in include files +(you may use separate object modules for this purpose too).

+

Example:

+

+

+
+        .ifref  ToHex                   ; If someone used this subroutine
+        ToHex:  tay                     ; Define subroutine
+                lda     HexTab,y
+                rts
+        .endif
+  
+
+
+

+

See also: +.REFERENCED

+ + +

11.59 .IMPORT +

+ + +

Import a symbol from another module. The command is followed by a comma +separated list of symbols to import, with each one optionally followed by +an address specification.

+

Example:

+

+

+
+        .import foo
+        .import bar: zeropage
+  
+
+
+

+

See: +.IMPORTZP

+ + +

11.60 .IMPORTZP +

+ + +

Import a symbol from another module. The command is followed by a comma +separated list of symbols to import. The symbols are explicitly imported +as zero page symbols (that is, symbols with values in byte range).

+

Example:

+

+

+
+        .importzp       foo, bar
+  
+
+
+

+

See: +.IMPORT

+ + +

11.61 .INCBIN +

+ + +

Include a file as binary data. The command expects a string argument +that is the name of a file to include literally in the current segment. +In addition to that, a start offset and a size value may be specified, +separated by commas. If no size is specified, all of the file from the +start offset to end-of-file is used. If no start position is specified +either, zero is assumed (which means that the whole file is inserted).

+

Example:

+

+

+
+        ; Include whole file
+        .incbin         "sprites.dat"
+
+        ; Include file starting at offset 256
+        .incbin         "music.dat", $100
+
+        ; Read 100 bytes starting at offset 200
+        .incbin         "graphics.dat", 200, 100
+  
+
+
+

+ + +

11.62 .INCLUDE +

+ + +

Include another file. Include files may be nested up to a depth of 16.

+

Example:

+

+

+
+        .include        "subs.inc"
+  
+
+
+

+ + +

11.63 .INTERRUPTOR +

+ + +

Export a symbol and mark it as an interruptor. This may be used together +with the linker to build a table of interruptor subroutines that are called +in an interrupt.

+

Note: The linker has a feature to build a table of marked routines, but it +is your code that must call these routines, so just declaring a symbol as +interruptor does nothing by itself.

+

An interruptor is always exported as an absolute (16 bit) symbol. You don't +need to use an additional .export statement, this is implied by +.interruptor. It may have an optional priority that is separated by a +comma. Higher numeric values mean a higher priority. If no priority is +given, the default priority of 7 is used. Be careful when assigning +priorities to your own module constructors so they won't interfere with the +ones in the cc65 library.

+

Example:

+

+

+
+        .interruptor    IrqHandler
+        .interruptor    Handler, 16
+  
+
+
+

+

See the +.CONDES command and the separate +section +Module constructors/destructors explaining +the feature in more detail.

+ + +

11.64 .LINECONT +

+ + +

Switch on or off line continuations using the backslash character +before a newline. The option is off by default. +Note: Line continuations do not work in a comment. A backslash at the +end of a comment is treated as part of the comment and does not trigger +line continuation. +The command must be followed by a '+' or '-' character to switch the +option on or off respectively.

+

Example:

+

+

+
+        .linecont       +               ; Allow line continuations
+
+        lda     \
+                #$20                    ; This is legal now
+  
+
+
+

+ + +

11.65 .LIST +

+ + +

Enable output to the listing. The command must be followed by a boolean +switch ("on", "off", "+" or "-") and will enable or disable listing +output. +The option has no effect if the listing is not enabled by the command line +switch -l. If -l is used, an internal counter is set to 1. Lines are output +to the listing file, if the counter is greater than zero, and suppressed if +the counter is zero. Each use of .LIST will increment or decrement the +counter.

+

Example:

+

+

+
+        .list   on              ; Enable listing output
+  
+
+
+

+ + +

11.66 .LISTBYTES +

+ + +

Set, how many bytes are shown in the listing for one source line. The +default is 12, so the listing will show only the first 12 bytes for any +source line that generates more than 12 bytes of code or data. +The directive needs an argument, which is either "unlimited", or an +integer constant in the range 4..255.

+

Examples:

+

+

+
+        .listbytes      unlimited       ; List all bytes
+        .listbytes      12              ; List the first 12 bytes
+        .incbin         "data.bin"      ; Include large binary file
+  
+
+
+

+ + +

11.67 .LOBYTES +

+ + +

Define byte sized data by extracting only the low byte (that is, bits 0-7) from +each expression. This is equivalent to +.BYTE with +the operator '<' prepended to each expression in its list.

+

Example:

+

+

+
+        .lobytes         $1234, $2345, $3456, $4567
+        .hibytes         $fedc, $edcb, $dcba, $cba9
+  
+
+
+

+

which is equivalent to

+

+

+
+        .byte            $34, $45, $56, $67
+        .byte            $fe, $ed, $dc, $cb
+  
+
+
+

+

Example:

+

+

+
+        .define MyTable TableItem0, TableItem1, TableItem2, TableItem3
+
+        TableLookupLo:   .lobytes MyTable
+        TableLookupHi:   .hibytes MyTable
+  
+
+
+

+

which is equivalent to

+

+

+
+        TableLookupLo:   .byte <TableItem0, <TableItem1, <TableItem2, <TableItem3
+        TableLookupHi:   .byte >TableItem0, >TableItem1, >TableItem2, >TableItem3
+  
+
+
+

+

See also: +.BYTE, + +.HIBYTES, + +.BANKBYTES

+ + +

11.68 .LOCAL +

+ + +

This command may only be used inside a macro definition. It declares a +list of identifiers as local to the macro expansion.

+

A problem when using macros are labels: Since they don't change their name, +you get a "duplicate symbol" error if the macro is expanded the second time. +Labels declared with +.LOCAL have their +name mapped to an internal unique name (___ABCD__) with each macro +invocation.

+

Some other assemblers start a new lexical block inside a macro expansion. +This has some drawbacks however, since that will not allow any symbol +to be visible outside a macro, a feature that is sometimes useful. The + +.LOCAL command is in my eyes a better way +to address the problem.

+

You get an error when using +.LOCAL outside +a macro.

+ + +

11.69 .LOCALCHAR +

+ + +

Defines the character that start "cheap" local labels. You may use one +of '@' and '?' as start character. The default is '@'.

+

Cheap local labels are labels that are visible only between two non +cheap labels. This way you can reuse identifiers like "loop" without +using explicit lexical nesting.

+

Example:

+

+

+
+        .localchar      '?'
+
+        Clear:  lda     #$00            ; Global label
+        ?Loop:  sta     Mem,y           ; Local label
+                dey
+                bne     ?Loop           ; Ok
+                rts
+        Sub:    ...                     ; New global label
+                bne     ?Loop           ; ERROR: Unknown identifier!
+  
+
+
+

+ + +

11.70 .MACPACK +

+ + +

Insert a predefined macro package. The command is followed by an +identifier specifying the macro package to insert. Available macro +packages are:

+

+

+
+        atari           Defines the scrcode macro.
+        cbm             Defines the scrcode macro.
+        cpu             Defines constants for the .CPU variable.
+        generic         Defines generic macros like add and sub.
+        longbranch      Defines conditional long jump macros.
+  
+
+
+

+

Including a macro package twice, or including a macro package that +redefines already existing macros will lead to an error.

+

Example:

+

+

+
+        .macpack        longbranch      ; Include macro package
+
+                cmp     #$20            ; Set condition codes
+                jne     Label           ; Jump long on condition
+  
+
+
+

+

Macro packages are explained in more detail in section +Macro packages.

+ + +

11.71 .MAC, .MACRO +

+ + +

Start a classic macro definition. The command is followed by an identifier +(the macro name) and optionally by a comma separated list of identifiers +that are macro parameters.

+

See section +Macros.

+ + +

11.72 .ORG +

+ + +

Start a section of absolute code. The command is followed by a constant +expression that gives the new PC counter location for which the code is +assembled. Use +.RELOC to switch back to +relocatable code.

+

By default, absolute/relocatable mode is global (valid even when switching +segments). Using .FEATURE +org_per_seg +it can be made segment local.

+

Please note that you do not need .ORG in most cases. Placing +code at a specific address is the job of the linker, not the assembler, so +there is usually no reason to assemble code to a specific address.

+

Example:

+

+

+
+        .org    $7FF            ; Emit code starting at $7FF
+  
+
+
+

+ + +

11.73 .OUT +

+ + +

Output a string to the console without producing an error. This command +is similar to .ERROR, however, it does not force an assembler error +that prevents the creation of an object file.

+

Example:

+

+

+
+        .out    "This code was written by the codebuster(tm)"
+  
+
+
+

+

See also the +.WARNING and +.ERROR directives.

+ + +

11.74 .P02 +

+ + +

Enable the 6502 instruction set, disable 65SC02, 65C02 and 65816 +instructions. This is the default if not overridden by the + +--cpu command line option.

+

See: +.PC02, +.PSC02 and +.P816

+ + +

11.75 .P816 +

+ + +

Enable the 65816 instruction set. This is a superset of the 65SC02 and +6502 instruction sets.

+

See: +.P02, +.PSC02 and +.PC02

+ + +

11.76 .PAGELEN, .PAGELENGTH +

+ + +

Set the page length for the listing. Must be followed by an integer +constant. The value may be "unlimited", or in the range 32 to 127. The +statement has no effect if no listing is generated. The default value is -1 +(unlimited) but may be overridden by the --pagelength command line +option. Beware: Since ca65 is a one pass assembler, the listing is generated +after assembly is complete, you cannot use multiple line lengths with one +source. Instead, the value set with the last .PAGELENGTH is used.

+

Examples:

+

+

+
+        .pagelength     66              ; Use 66 lines per listing page
+
+        .pagelength     unlimited       ; Unlimited page length
+  
+
+
+

+ + +

11.77 .PC02 +

+ + +

Enable the 65C02 instructions set. This instruction set includes all +6502 and 65SC02 instructions.

+

See: +.P02, +.PSC02 and +.P816

+ + +

11.78 .POPSEG +

+ + +

Pop the last pushed segment from the stack, and set it.

+

This command will switch back to the segment that was last pushed onto the +segment stack using the +.PUSHSEG +command, and remove this entry from the stack.

+

The assembler will print an error message if the segment stack is empty +when this command is issued.

+

See: +.PUSHSEG

+ + +

11.79 .PROC +

+ + +

Start a nested lexical level with the given name and adds a symbol with this +name to the enclosing scope. All new symbols from now on are in the local +lexical level and are accessible from outside only via +explicit scope specification. Symbols defined outside this local +level may be accessed as long as their names are not used for new symbols +inside the level. Symbols names in other lexical levels do not clash, so you +may use the same names for identifiers. The lexical level ends when the + +.ENDPROC command is read. Lexical levels +may be nested up to a depth of 16 (this is an artificial limit to protect +against errors in the source).

+

Note: Macro names are always in the global level and in a separate name +space. There is no special reason for this, it's just that I've never +had any need for local macro definitions.

+

Example:

+

+

+
+        .proc   Clear           ; Define Clear subroutine, start new level
+                lda     #$00
+        L1:     sta     Mem,y   ; L1 is local and does not cause a
+                                ; duplicate symbol error if used in other
+                                ; places
+                dey
+                bne     L1      ; Reference local symbol
+                rts
+        .endproc                ; Leave lexical level
+  
+
+
+

+

See: +.ENDPROC and +.SCOPE

+ + +

11.80 .PSC02 +

+ + +

Enable the 65SC02 instructions set. This instruction set includes all +6502 instructions.

+

See: +.P02, +.PC02 and +.P816

+ + +

11.81 .PUSHSEG +

+ + +

Push the currently active segment onto a stack. The entries on the stack +include the name of the segment and the segment type. The stack has a size +of 16 entries.

+

.PUSHSEG allows together with +.POPSEG +to switch to another segment and to restore the old segment later, without +even knowing the name and type of the current segment.

+

The assembler will print an error message if the segment stack is already +full, when this command is issued.

+

See: +.POPSEG

+ + +

11.82 .RELOC +

+ + +

Switch back to relocatable mode. See the +.ORG command.

+ + +

11.83 .REPEAT +

+ + +

Repeat all commands between .REPEAT and +.ENDREPEAT constant number of times. The command is followed by +a constant expression that tells how many times the commands in the body +should get repeated. Optionally, a comma and an identifier may be specified. +If this identifier is found in the body of the repeat statement, it is +replaced by the current repeat count (starting with zero for the first time +the body is repeated).

+

.REPEAT statements may be nested. If you use the same repeat count +identifier for a nested .REPEAT statement, the one from the inner +level will be used, not the one from the outer level.

+

Example:

+

The following macro will emit a string that is "encrypted" in that all +characters of the string are XORed by the value $55.

+

+

+
+        .macro  Crypt   Arg
+                .repeat .strlen(Arg), I
+                .byte   .strat(Arg, I) ^ $55
+                .endrep
+        .endmacro
+  
+
+
+

+

See: +.ENDREPEAT

+ + +

11.84 .RES +

+ + +

Reserve storage. The command is followed by one or two constant +expressions. The first one is mandatory and defines, how many bytes of +storage should be defined. The second, optional expression must by a +constant byte value that will be used as value of the data. If there +is no fill value given, the linker will use the value defined in the +linker configuration file (default: zero).

+

Example:

+

+

+
+        ; Reserve 12 bytes of memory with value $AA
+        .res    12, $AA
+  
+
+
+

+ + +

11.85 .RODATA +

+ + +

Switch to the RODATA segment. The name of the RODATA segment is always +"RODATA", so this is a shortcut for

+

+

+
+        .segment  "RODATA"
+  
+
+
+

+

The RODATA segment is a segment that is used by the compiler for +readonly data like string constants.

+

See also the +.SEGMENT command.

+ + +

11.86 .SCOPE +

+ + +

Start a nested lexical level with the given name. All new symbols from now +on are in the local lexical level and are accessible from outside only via +explicit scope specification. Symbols defined +outside this local level may be accessed as long as their names are not used +for new symbols inside the level. Symbols names in other lexical levels do +not clash, so you may use the same names for identifiers. The lexical level +ends when the +.ENDSCOPE command is +read. Lexical levels may be nested up to a depth of 16 (this is an +artificial limit to protect against errors in the source).

+

Note: Macro names are always in the global level and in a separate name +space. There is no special reason for this, it's just that I've never +had any need for local macro definitions.

+

Example:

+

+

+
+        .scope  Error                   ; Start new scope named Error
+                None = 0                ; No error
+                File = 1                ; File error
+                Parse = 2               ; Parse error
+        .endscope                       ; Close lexical level
+
+                ...
+                lda #Error::File        ; Use symbol from scope Error
+  
+
+
+

+

See: +.ENDSCOPE and +.PROC

+ + +

11.87 .SEGMENT +

+ + +

Switch to another segment. Code and data is always emitted into a +segment, that is, a named section of data. The default segment is +"CODE". There may be up to 254 different segments per object file +(and up to 65534 per executable). There are shortcut commands for +the most common segments ("CODE", "DATA" and "BSS").

+

The command is followed by a string containing the segment name (there are +some constraints for the name - as a rule of thumb use only those segment +names that would also be valid identifiers). There may also be an optional +address size separated by a colon. See the section covering +address sizes for more information.

+

The default address size for a segment depends on the memory model specified +on the command line. The default is "absolute", which means that you don't +have to use an address size modifier in most cases.

+

"absolute" means that the is a segment with 16 bit (absolute) addressing. +That is, the segment will reside somewhere in core memory outside the zero +page. "zeropage" (8 bit) means that the segment will be placed in the zero +page and direct (short) addressing is possible for data in this segment.

+

Beware: Only labels in a segment with the zeropage attribute are marked +as reachable by short addressing. The `*' (PC counter) operator will +work as in other segments and will create absolute variable values.

+

Please note that a segment cannot have two different address sizes. A +segment specified as zeropage cannot be declared as being absolute later.

+

Examples:

+

+

+
+        .segment "ROM2"                 ; Switch to ROM2 segment
+        .segment "ZP2": zeropage        ; New direct segment
+        .segment "ZP2"                  ; Ok, will use last attribute
+        .segment "ZP2": absolute        ; Error, redecl mismatch
+  
+
+
+

+

See: +.BSS, +.CODE, +.DATA and +.RODATA

+ + +

11.88 .SETCPU +

+ + +

Switch the CPU instruction set. The command is followed by a string that +specifies the CPU. Possible values are those that can also be supplied to +the +--cpu command line option, +namely: 6502, 6502X, 65SC02, 65C02, 65816, sunplus and HuC6280. Please +note that support for the sunplus CPU is not available in the freeware +version, because the instruction set of the sunplus CPU is "proprietary +and confidential".

+

See: +.CPU, + +.IFP02, + +.IFP816, + +.IFPC02, + +.IFPSC02, + +.P02, + +.P816, + +.PC02, + +.PSC02

+ + +

11.89 .SMART +

+ + +

Switch on or off smart mode. The command must be followed by a '+' or '-' +character to switch the option on or off respectively. The default is off +(that is, the assembler doesn't try to be smart), but this default may be +changed by the -s switch on the command line.

+

In smart mode the assembler will do the following:

+

+

    +
  • Track usage of the REP and SEP instructions in 65816 mode +and update the operand sizes accordingly. If the operand of such an +instruction cannot be evaluated by the assembler (for example, because +the operand is an imported symbol), a warning is issued. Beware: Since +the assembler cannot trace the execution flow this may lead to false +results in some cases. If in doubt, use the .Inn and .Ann +instructions to tell the assembler about the current settings.
  • +
  • In 65816 mode, replace a RTS instruction by RTL if it is +used within a procedure declared as far, or if the procedure has +no explicit address specification, but it is far because of the +memory model used.
  • +
+

+

Example:

+

+

+
+        .smart                          ; Be smart
+        .smart  -                       ; Stop being smart
+  
+
+
+

+

See: +.A16, + +.A8, + +.I16, + +.I8

+ + +

11.90 .STRUCT +

+ + +

Starts a struct definition. Structs are covered in a separate section named +"Structs and unions".

+

See: +.ENDSTRUCT

+ + +

11.91 .SUNPLUS +

+ + +

Enable the SunPlus instructions set. This command will not work in the +freeware version of the assembler, because the instruction set is +"proprietary and confidential".

+

See: +.P02, +.PSC02, +.PC02, and + +.P816

+ + +

11.92 .TAG +

+ + +

Allocate space for a struct or union.

+

Example:

+

+

+
+        .struct Point
+                xcoord  .word
+                ycoord  .word
+        .endstruct
+
+        .bss
+                .tag    Point           ; Allocate 4 bytes
+  
+
+
+

+ + +

11.93 .WARNING +

+ + +

Force an assembly warning. The assembler will output a warning message +preceded by "User warning". This warning will always be output, even if +other warnings are disabled with the +-W0 +command line option.

+

This command may be used to output possible problems when assembling +the source file.

+

Example:

+

+

+
+        .macro  jne     target
+                .local L1
+                .ifndef target
+                .warning "Forward jump in jne, cannot optimize!"
+                beq     L1
+                jmp     target
+        L1:
+                .else
+                ...
+                .endif
+        .endmacro
+  
+
+
+

+

See also the +.ERROR and +.OUT directives.

+ + +

11.94 .WORD +

+ + +

Define word sized data. Must be followed by a sequence of (word ranged, +but not necessarily constant) expressions.

+

Example:

+

+

+
+        .word   $0D00, $AF13, _Clear
+  
+
+
+

+ + +

11.95 .ZEROPAGE +

+ + +

Switch to the ZEROPAGE segment and mark it as direct (zeropage) segment. +The name of the ZEROPAGE segment is always "ZEROPAGE", so this is a +shortcut for

+

+

+
+        .segment  "ZEROPAGE", zeropage
+  
+
+
+

+

Because of the "zeropage" attribute, labels declared in this segment are +addressed using direct addressing mode if possible. You must instruct +the linker to place this segment somewhere in the address range 0..$FF +otherwise you will get errors.

+

See: +.SEGMENT

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-12.html b/ca65-primer/doc/ca65-12.html new file mode 100755 index 0000000..6b9aa9f --- /dev/null +++ b/ca65-primer/doc/ca65-12.html @@ -0,0 +1,489 @@ + + + + + ca65 Users Guide: Macros + + + + + +Next +Previous +Contents +
+

12. Macros

+ + + + +

12.1 Introduction +

+ + +

Macros may be thought of as "parametrized super instructions". Macros are +sequences of tokens that have a name. If that name is used in the source +file, the macro is "expanded", that is, it is replaced by the tokens that +were specified when the macro was defined.

+ + +

12.2 Macros without parameters +

+ + +

In it's simplest form, a macro does not have parameters. Here's an +example:

+

+

+
+        .macro  asr             ; Arithmetic shift right
+                cmp     #$80    ; Put bit 7 into carry
+                ror             ; Rotate right with carry
+        .endmacro
+
+
+

+

The macro above consists of two real instructions, that are inserted into +the code, whenever the macro is expanded. Macro expansion is simply done +by using the name, like this:

+

+

+
+        lda     $2010
+        asr
+        sta     $2010
+
+
+

+ + +

12.3 Parametrized macros +

+ + +

When using macro parameters, macros can be even more useful:

+

+

+
+        .macro  inc16   addr
+                clc
+                lda     addr
+                adc     #$01
+                sta     addr
+                lda     addr+1
+                adc     #$00
+                sta     addr+1
+        .endmacro
+
+
+

+

When calling the macro, you may give a parameter, and each occurrence of +the name "addr" in the macro definition will be replaced by the given +parameter. So

+

+

+
+        inc16   $1000
+
+
+

+

will be expanded to

+

+

+
+                clc
+                lda     $1000
+                adc     #$01
+                sta     $1000
+                lda     $1000+1
+                adc     #$00
+                sta     $1000+1
+
+
+

+

A macro may have more than one parameter, in this case, the parameters +are separated by commas. You are free to give less parameters than the +macro actually takes in the definition. You may also leave intermediate +parameters empty. Empty parameters are replaced by empty space (that is, +they are removed when the macro is expanded). If you have a look at our +macro definition above, you will see, that replacing the "addr" parameter +by nothing will lead to wrong code in most lines. To help you, writing +macros with a variable parameter list, there are some control commands:

+

+.IFBLANK tests the rest of the line and +returns true, if there are any tokens on the remainder of the line. Since +empty parameters are replaced by nothing, this may be used to test if a given +parameter is empty. +.IFNBLANK tests the +opposite.

+

Look at this example:

+

+

+
+        .macro  ldaxy   a, x, y
+        .ifnblank       a
+                lda     #a
+        .endif
+        .ifnblank       x
+                ldx     #x
+        .endif
+        .ifnblank       y
+                ldy     #y
+        .endif
+        .endmacro
+
+
+

+

This macro may be called as follows:

+

+

+
+        ldaxy   1, 2, 3         ; Load all three registers
+
+        ldaxy   1, , 3          ; Load only a and y
+
+        ldaxy   , , 3           ; Load y only
+
+
+

+

There's another helper command for determining, which macro parameters are +valid: +.PARAMCOUNT This command is +replaced by the parameter count given, including intermediate empty macro +parameters:

+

+

+
+        ldaxy   1               ; .PARAMCOUNT = 1
+        ldaxy   1,,3            ; .PARAMCOUNT = 3
+        ldaxy   1,2             ; .PARAMCOUNT = 2
+        ldaxy   1,              ; .PARAMCOUNT = 2
+        ldaxy   1,2,3           ; .PARAMCOUNT = 3
+
+
+

+

Macro parameters may optionally be enclosed into curly braces. This allows the +inclusion of tokens that would otherwise terminate the parameter (the comma in +case of a macro parameter).

+

+

+
+        .macro  foo     arg1, arg2
+                ...
+        .endmacro
+
+                foo     ($00,x)         ; Two parameters passed
+                foo     {($00,x)}       ; One parameter passed
+
+
+

+

In the first case, the macro is called with two parameters: '($00' +and 'x)'. The comma is not passed to the macro, since it is part of the +calling sequence, not the parameters.

+

In the second case, '($00,x)' is passed to the macro, this time +including the comma.

+ + +

12.4 Detecting parameter types +

+ + +

Sometimes it is nice to write a macro that acts differently depending on the +type of the argument supplied. An example would be a macro that loads a 16 bit +value from either an immediate operand, or from memory. The +.MATCH and +.XMATCH +functions will allow you to do exactly this:

+

+

+
+        .macro  ldax    arg
+                .if (.match (.left (1, {arg}), #))
+                    ; immediate mode
+                    lda     #<(.right (.tcount ({arg})-1, {arg}))
+                    ldx     #>(.right (.tcount ({arg})-1, {arg}))
+                .else
+                    ; assume absolute or zero page
+                    lda     arg
+                    ldx     1+(arg)
+                .endif
+        .endmacro
+
+
+

+

Using the +.MATCH function, the macro is able to +check if its argument begins with a hash mark. If so, two immediate loads are +emitted, Otherwise a load from an absolute zero page memory location is +assumed. Please note how the curly braces are used to enclose parameters to +pseudo functions handling token lists. This is necessary, because the token +lists may include commas or parens, which would be treated by the assembler +as end-of-list.

+

The macro can be used as

+

+

+
+        foo:    .word   $5678
+        ...
+                ldax    #$1234          ; X=$12, A=$34
+        ...
+                ldax    foo             ; X=$56, A=$78
+
+
+

+ + +

12.5 Recursive macros +

+ + +

Macros may be used recursively:

+

+

+
+        .macro  push    r1, r2, r3
+                lda     r1
+                pha
+        .if     .paramcount > 1
+                push    r2, r3
+        .endif
+        .endmacro
+
+
+

+

There's also a special macro to help writing recursive macros: +.EXITMACRO This command will stop macro expansion +immediately:

+

+

+
+        .macro  push    r1, r2, r3, r4, r5, r6, r7
+        .ifblank        r1
+                ; First parameter is empty
+                .exitmacro
+        .else
+                lda     r1
+                pha
+        .endif
+                push    r2, r3, r4, r5, r6, r7
+        .endmacro
+
+
+

+

When expanding this macro, the expansion will push all given parameters +until an empty one is encountered. The macro may be called like this:

+

+

+
+        push    $20, $21, $32           ; Push 3 ZP locations
+        push    $21                     ; Push one ZP location
+
+
+

+ + +

12.6 Local symbols inside macros +

+ + +

Now, with recursive macros, +.IFBLANK and + +.PARAMCOUNT, what else do you need? +Have a look at the inc16 macro above. Here is it again:

+

+

+
+        .macro  inc16   addr
+                clc
+                lda     addr
+                adc     #$01
+                sta     addr
+                lda     addr+1
+                adc     #$00
+                sta     addr+1
+        .endmacro
+
+
+

+

If you have a closer look at the code, you will notice, that it could be +written more efficiently, like this:

+

+

+
+        .macro  inc16   addr
+                inc     addr
+                bne     Skip
+                inc     addr+1
+        Skip:
+        .endmacro
+
+
+

+

But imagine what happens, if you use this macro twice? Since the label +"Skip" has the same name both times, you get a "duplicate symbol" error. +Without a way to circumvent this problem, macros are not as useful, as +they could be. One solution is, to start a new lexical block inside the +macro:

+

+

+
+        .macro  inc16   addr
+        .proc
+                inc     addr
+                bne     Skip
+                inc     addr+1
+        Skip:
+        .endproc
+        .endmacro
+
+
+

+

Now the label is local to the block and not visible outside. However, +sometimes you want a label inside the macro to be visible outside. To make +that possible, there's a new command that's only usable inside a macro +definition: +.LOCAL. .LOCAL declares one +or more symbols as local to the macro expansion. The names of local variables +are replaced by a unique name in each separate macro expansion. So we could +also solve the problem above by using .LOCAL:

+

+

+
+        .macro  inc16   addr
+                .local  Skip            ; Make Skip a local symbol
+                clc
+                lda     addr
+                adc     #$01
+                sta     addr
+                bcc     Skip
+                inc     addr+1
+        Skip:                           ; Not visible outside
+        .endmacro
+
+
+

+ + +

12.7 C style macros +

+ + +

Starting with version 2.5 of the assembler, there is a second macro type +available: C style macros using the .DEFINE directive. These macros are +similar to the classic macro type described above, but behaviour is sometimes +different:

+

+

    +
  • Macros defined with +.DEFINE may not +span more than a line. You may use line continuation (see +.LINECONT) to spread the definition over +more than one line for increased readability, but the macro itself +may not contain an end-of-line token. +
  • +
  • Macros defined with +.DEFINE share +the name space with classic macros, but they are detected and replaced +at the scanner level. While classic macros may be used in every place, +where a mnemonic or other directive is allowed, +.DEFINE style macros are allowed anywhere in a line. So +they are more versatile in some situations. +
  • +
  • +.DEFINE style macros may take +parameters. While classic macros may have empty parameters, this is +not true for +.DEFINE style macros. +For this macro type, the number of actual parameters must match +exactly the number of formal parameters. + +To make this possible, formal parameters are enclosed in braces when +defining the macro. If there are no parameters, the empty braces may +be omitted. +
  • +
  • Since +.DEFINE style macros may not +contain end-of-line tokens, there are things that cannot be done. They +may not contain several processor instructions for example. So, while +some things may be done with both macro types, each type has special +usages. The types complement each other. +
  • +
+

+

Let's look at a few examples to make the advantages and disadvantages +clear.

+

To emulate assemblers that use "EQU" instead of "=" you may use the +following .DEFINE:

+

+

+
+        .define EQU     =
+
+        foo     EQU     $1234           ; This is accepted now
+
+
+

+

You may use the directive to define string constants used elsewhere:

+

+

+
+        ; Define the version number
+        .define VERSION         "12.3a"
+
+        ; ... and use it
+        .asciiz VERSION
+
+
+

+

Macros with parameters may also be useful:

+

+

+
+        .define DEBUG(message)  .out    message
+
+        DEBUG   "Assembling include file #3"
+
+
+

+

Note that, while formal parameters have to be placed in braces, this is +not true for the actual parameters. Beware: Since the assembler cannot +detect the end of one parameter, only the first token is used. If you +don't like that, use classic macros instead:

+

+

+
+        .macro  message
+                .out    message
+        .endmacro
+
+
+

+

(This is an example where a problem can be solved with both macro types).

+ + +

12.8 Characters in macros +

+ + +

When using the +-t option, characters are translated +into the target character set of the specific machine. However, this happens +as late as possible. This means that strings are translated if they are part +of a +.BYTE or +.ASCIIZ command. Characters are translated as soon as they are +used as part of an expression.

+

This behaviour is very intuitive outside of macros but may be confusing when +doing more complex macros. If you compare characters against numeric values, +be sure to take the translation into account.

+ + + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-13.html b/ca65-primer/doc/ca65-13.html new file mode 100755 index 0000000..23c71ef --- /dev/null +++ b/ca65-primer/doc/ca65-13.html @@ -0,0 +1,168 @@ + + + + + ca65 Users Guide: Macro packages + + + + + +Next +Previous +Contents +
+

13. Macro packages

+ + +

Using the +.MACPACK directive, predefined +macro packages may be included with just one command. Available macro packages +are:

+ + +

13.1 .MACPACK generic +

+ + +

This macro package defines macros that are useful in almost any program. +Currently, two macros are defined:

+

+

+
+        .macro  add     Arg
+                clc
+                adc     Arg
+        .endmacro
+
+        .macro  sub     Arg
+                sec
+                sbc     Arg
+        .endmacro
+
+
+

+ + +

13.2 .MACPACK longbranch +

+ + +

This macro package defines long conditional jumps. They are named like the +short counterpart but with the 'b' replaced by a 'j'. Here is a sample +definition for the "jeq" macro, the other macros are built using the same +scheme:

+

+

+
+        .macro  jeq     Target
+                .if     .def(Target) .and ((*+2)-(Target) <= 127)
+                beq     Target
+                .else
+                bne     *+5
+                jmp     Target
+                .endif
+        .endmacro
+
+
+

+

All macros expand to a short branch, if the label is already defined (back +jump) and is reachable with a short jump. Otherwise the macro expands to a +conditional branch with the branch condition inverted, followed by an absolute +jump to the actual branch target.

+

The package defines the following macros:

+

+

+
+        jeq, jne, jmi, jpl, jcs, jcc, jvs, jvc
+
+
+

+ + + +

13.3 .MACPACK cbm +

+ + +

The cbm macro package will define a macro named scrcode. It takes a +string as argument and places this string into memory translated into screen +codes.

+ + +

13.4 .MACPACK cpu +

+ + +

This macro package does not define any macros but constants used to examine +the value read from the +.CPU pseudo variable. For +each supported CPU a constant similar to

+

+

+
+    CPU_6502
+    CPU_65SC02
+    CPU_65C02
+    CPU_65816
+    CPU_SUNPLUS
+    CPU_SWEET16
+    CPU_HUC6280
+
+
+

+

is defined. These constants may be used to determine the exact type of the +currently enabled CPU. In addition to that, for each CPU instruction set, +another constant is defined:

+

+

+
+    CPU_ISET_6502
+    CPU_ISET_65SC02
+    CPU_ISET_65C02
+    CPU_ISET_65816
+    CPU_ISET_SUNPLUS
+    CPU_ISET_SWEET16
+    CPU_ISET_HUC6280
+
+
+

+

The value read from the +.CPU pseudo variable may +be checked with +.BITAND to determine if the +currently enabled CPU supports a specific instruction set. For example the +65C02 supports all instructions of the 65SC02 CPU, so it has the +CPU_ISET_65SC02 bit set in addition to its native CPU_ISET_65C02 +bit. Using

+

+

+
+        .if (.cpu .bitand CPU_ISET_65SC02)
+                lda     (sp)
+        .else
+                ldy     #$00
+                lda     (sp),y
+        .endif
+
+
+

+

it is possible to determine if the

+

+

+
+                lda     (sp)
+
+
+

+

instruction is supported, which is the case for the 65SC02, 65C02 and 65816 +CPUs (the latter two are upwards compatible to the 65SC02).

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-14.html b/ca65-primer/doc/ca65-14.html new file mode 100755 index 0000000..7630fc5 --- /dev/null +++ b/ca65-primer/doc/ca65-14.html @@ -0,0 +1,49 @@ + + + + + ca65 Users Guide: Predefined constants + + + + + +Next +Previous +Contents +
+

14. Predefined constants

+ + +

For better orthogonality, the assembler defines similar symbols as the +compiler, depending on the target system selected:

+

+

    +
  • __APPLE2__ - Target system is apple2
  • +
  • __APPLE2ENH__ - Target system is apple2enh
  • +
  • __ATARI__ - Target system is atari
  • +
  • __ATMOS__ - Target system is atmos
  • +
  • __BBC__ - Target system is bbc
  • +
  • __C128__ - Target system is c128
  • +
  • __C16__ - Target system is c16
  • +
  • __C64__ - Target system is c64
  • +
  • __CBM__ - Target is a Commodore system
  • +
  • __CBM510__ - Target system is cbm510
  • +
  • __CBM610__ - Target system is cbm610
  • +
  • __GEOS__ - Target system is geos
  • +
  • __LUNIX__ - Target system is lunix
  • +
  • __NES__ - Target system is nes
  • +
  • __PET__ - Target system is pet
  • +
  • __PLUS4__ - Target system is plus4
  • +
  • __SUPERVISION__ - Target system is supervision
  • +
  • __VIC20__ - Target system is vic20
  • +
+

+ + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-15.html b/ca65-primer/doc/ca65-15.html new file mode 100755 index 0000000..5d83c5d --- /dev/null +++ b/ca65-primer/doc/ca65-15.html @@ -0,0 +1,133 @@ + + + + + ca65 Users Guide: Structs and unions + + + + + +Next +Previous +Contents +
+

15. Structs and unions

+ + + +

15.1 Structs and unions Overview +

+ + +

Structs and unions are special forms of +scopes. They +are to some degree comparable to their C counterparts. Both have a list of +members. Each member allocates storage and may optionally have a name, which, +in case of a struct, is the offset from the beginning and, in case of a union, +is always zero.

+ + +

15.2 Declaration +

+ + +

Here is an example for a very simple struct with two members and a total size +of 4 bytes:

+

+

+
+      .struct Point
+              xcoord  .word
+              ycoord  .word
+      .endstruct
+
+
+

+

A union shares the total space between all its members, its size is the same +as that of the largest member.

+

A struct or union must not necessarily have a name. If it is anonymous, no +local scope is opened, the identifiers used to name the members are placed +into the current scope instead.

+

A struct may contain unnamed members and definitions of local structs. The +storage allocators may contain a multiplier, as in the example below:

+

+

+
+      .struct Circle
+              .struct Point
+                      .word   2         ; Allocate two words
+              .endstruct
+              Radius  .word
+      .endstruct
+
+
+

+ + +

15.3 The .TAG keyword +

+ + +

Using the +.TAG keyword, it is possible to reserve space +for an already defined struct or unions within another struct:

+

+

+
+      .struct Point
+              xcoord  .word
+              ycoord  .word
+      .endstruct
+
+      .struct Circle
+              Origin  .tag    Point
+              Radius  .byte
+      .endstruct
+
+
+

+

Space for a struct or union may be allocated using the +.TAG directive.

+

+

+
+        C:      .tag    Circle
+
+
+

+

Currently, members are just offsets from the start of the struct or union. To +access a field of a struct, the member offset has to be added to the address +of the struct itself:

+

+

+
+        lda     C+Circle::Radius        ; Load circle radius into A
+
+
+

+

This may change in a future version of the assembler.

+ + +

15.4 Limitations +

+ + +

Structs and unions are currently implemented as nested symbol tables (in fact, +they were a by-product of the improved scoping rules). Currently, the +assembler has no idea of types. This means that the +.TAG keyword will only allocate space. You won't be able to initialize +variables declared with +.TAG, and adding an embedded +structure to another structure with +.TAG will not make +this structure accessible by using the '::' operator.

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-16.html b/ca65-primer/doc/ca65-16.html new file mode 100755 index 0000000..607ef85 --- /dev/null +++ b/ca65-primer/doc/ca65-16.html @@ -0,0 +1,129 @@ + + + + + ca65 Users Guide: Module constructors/destructors + + + + + +Next +Previous +Contents +
+

16. Module constructors/destructors

+ + +

Note: This section applies mostly to C programs, so the explanation +below uses examples from the C libraries. However, the feature may also be +useful for assembler programs.

+ + +

16.1 Module constructors/destructors Overview +

+ + +

Using the +.CONSTRUCTOR, +.DESTRUCTOR and +.INTERRUPTOR keywords it it possible to export functions in a +special way. The linker is able to generate tables with all functions of a +specific type. Such a table will only include symbols from object +files that are linked into a specific executable. This may be used to add +initialization and cleanup code for library modules, or a table of interrupt +handler functions.

+

The C heap functions are an example where module initialization code is used. +All heap functions (malloc, free, ...) work with a few +variables that contain the start and the end of the heap, pointers to the free +list and so on. Since the end of the heap depends on the size and start of the +stack, it must be initialized at runtime. However, initializing these +variables for programs that do not use the heap are a waste of time and +memory.

+

So the central module defines a function that contains initialization code and +exports this function using the .CONSTRUCTOR statement. If (and only if) +this module is added to an executable by the linker, the initialization +function will be placed into the table of constructors by the linker. The C +startup code will call all constructors before main and all destructors +after main, so without any further work, the heap initialization code is +called once the module is linked in.

+

While it would be possible to add explicit calls to initialization functions +in the startup code, the new approach has several advantages:

+

+

    +
  1. If a module is not included, the initialization code is not linked in and not +called. So you don't pay for things you don't need. +
  2. +
  3. Adding another library that needs initialization does not mean that the +startup code has to be changed. Before we had module constructors and +destructors, the startup code for all systems had to be adjusted to call the +new initialization code. +
  4. +
  5. The feature saves memory: Each additional initialization function needs just +two bytes in the table (a pointer to the function). +
  6. +
+

+ + +

16.2 Calling order +

+ + +

The symbols are sorted in increasing priority order by the linker when using +one of the builtin linker configurations, so the functions with lower +priorities come first and are followed by those with higher priorities. The C +library runtime subroutine that walks over the function tables calls the +functions starting from the top of the table - which means that functions with +a high priority are called first.

+

So when using the C runtime, functions are called with high priority functions +first, followed by low priority functions.

+ + +

16.3 Pitfalls +

+ + +

When using these special symbols, please take care of the following:

+

+

    +
  • The linker will only generate function tables, it will not generate code to +call these functions. If you're using the feature in some other than the +existing C environments, you have to write code to call all functions in a +linker generated table yourself. See the condes and callirq modules +in the C runtime for an example on how to do this. +
  • +
  • The linker will only add addresses of functions that are in modules linked to +the executable. This means that you have to be careful where to place the +condes functions. If initialization or an irq handler is needed for a group of +functions, be sure to place the function into a module that is linked in +regardless of which function is called by the user. +
  • +
  • The linker will generate the tables only when requested to do so by the +FEATURE CONDES statement in the linker config file. Each table has to +be requested separately. +
  • +
  • Constructors and destructors may have priorities. These priorities determine +the order of the functions in the table. If your initialization or cleanup code +does depend on other initialization or cleanup code, you have to choose the +priority for the functions accordingly. +
  • +
  • Besides the +.CONSTRUCTOR, +.DESTRUCTOR and +.INTERRUPTOR statements, there is also a more generic command: + +.CONDES. This allows to specify an +additional type. Predefined types are 0 (constructor), 1 (destructor) and 2 +(interruptor). The linker generates a separate table for each type on request. +
  • +
+

+ + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-17.html b/ca65-primer/doc/ca65-17.html new file mode 100755 index 0000000..282277d --- /dev/null +++ b/ca65-primer/doc/ca65-17.html @@ -0,0 +1,122 @@ + + + + + ca65 Users Guide: Porting sources from other assemblers + + + + + +Next +Previous +Contents +
+

17. Porting sources from other assemblers

+ + +

Sometimes it is necessary to port code written for older assemblers to ca65. +In some cases, this can be done without any changes to the source code by +using the emulation features of ca65 (see +.FEATURE). In other cases, it is necessary to make changes to the +source code.

+

Probably the biggest difference is the handling of the +.ORG directive. ca65 generates relocatable code, and placement is +done by the linker. Most other assemblers generate absolute code, placement is +done within the assembler and there is no external linker.

+

In general it is not a good idea to write new code using the emulation +features of the assembler, but there may be situations where even this rule is +not valid.

+ +

17.1 TASS +

+ + +

You need to use some of the ca65 emulation features to simulate the behaviour +of such simple assemblers.

+

+

    +
  1. Prepare your sourcecode like this: + +
    +
    +        ; if you want TASS style labels without colons
    +        .feature labels_without_colons
    +
    +        ; if you want TASS style character constants
    +        ; ("a" instead of the default 'a')
    +        .feature loose_char_term
    +
    +                .word *+2       ; the cbm load address
    +
    +                [yourcode here]
    +
    +
    + + +notice that the two emulation features are mostly useful for porting +sources originally written in/for TASS, they are not needed for the +actual "simple assembler operation" and are not recommended if you are +writing new code from scratch. +
  2. +
  3. Replace all program counter assignments (which are not possible in ca65 +by default, and the respective emulation feature works different from what +you'd expect) by another way to skip to memory locations, for example the + +.RES directive. + +
    +
    +        ; *=$2000
    +        .res $2000-*    ; reserve memory up to $2000
    +
    +
    + + +Please note that other than the original TASS, ca65 can never move the program +counter backwards - think of it as if you are assembling to disk with TASS. +
  4. +
  5. Conditional assembly (.ifeq/.endif/.goto etc.) must be +rewritten to match ca65 syntax. Most importantly notice that due to the lack +of .goto, everything involving loops must be replaced by + +.REPEAT. +
  6. +
  7. To assemble code to a different address than it is executed at, use the + +.ORG directive instead of +.offs-constructs. + +
    +
    +        .org $1800
    +
    +        [floppy code here]
    +
    +        .reloc  ; back to normal
    +
    +
    + +
  8. +
  9. Then assemble like this: + +
    +
    +        cl65 --start-addr 0x0ffe -t none myprog.s -o myprog.prg
    +
    +
    + + +Note that you need to use the actual start address minus two, since two bytes +are used for the cbm load address. +
  10. +
+

+ + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-18.html b/ca65-primer/doc/ca65-18.html new file mode 100755 index 0000000..2ffb75f --- /dev/null +++ b/ca65-primer/doc/ca65-18.html @@ -0,0 +1,31 @@ + + + + + ca65 Users Guide: Bugs/Feedback + + + + + +Next +Previous +Contents +
+

18. Bugs/Feedback

+ + +

If you have problems using the assembler, if you find any bugs, or if +you're doing something interesting with the assembler, I would be glad to +hear from you. Feel free to contact me by email +( +uz@cc65.org).

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-19.html b/ca65-primer/doc/ca65-19.html new file mode 100755 index 0000000..3477c3a --- /dev/null +++ b/ca65-primer/doc/ca65-19.html @@ -0,0 +1,46 @@ + + + + + ca65 Users Guide: Copyright + + + + +Next +Previous +Contents +
+

19. Copyright

+ + +

ca65 (and all cc65 binutils) are (C) Copyright 1998-2003 Ullrich von +Bassewitz. For usage of the binaries and/or sources the following +conditions do apply:

+

This software is provided 'as-is', without any expressed or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software.

+

Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions:

+

+

    +
  1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required.
  2. +
  3. Altered source versions must be plainly marked as such, and must not +be misrepresented as being the original software.
  4. +
  5. This notice may not be removed or altered from any source +distribution.
  6. +
+

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-2.html b/ca65-primer/doc/ca65-2.html new file mode 100755 index 0000000..9419de6 --- /dev/null +++ b/ca65-primer/doc/ca65-2.html @@ -0,0 +1,244 @@ + + + + + ca65 Users Guide: Usage + + + + + +Next +Previous +Contents +
+

2. Usage

+ + + + +

2.1 Command line option overview +

+ + +

The assembler accepts the following options:

+

+

+
+---------------------------------------------------------------------------
+Usage: ca65 [options] file
+Short options:
+  -D name[=value]       Define a symbol
+  -I dir                Set an include directory search path
+  -U                    Mark unresolved symbols as import
+  -V                    Print the assembler version
+  -W n                  Set warning level n
+  -g                    Add debug info to object file
+  -h                    Help (this text)
+  -i                    Ignore case of symbols
+  -l                    Create a listing if assembly was ok
+  -mm model             Set the memory model
+  -o name               Name the output file
+  -s                    Enable smart mode
+  -t sys                Set the target system
+  -v                    Increase verbosity
+
+Long options:
+  --auto-import         Mark unresolved symbols as import
+  --cpu type            Set cpu type
+  --debug-info          Add debug info to object file
+  --feature name        Set an emulation feature
+  --forget-inc-paths    Forget include search paths
+  --help                Help (this text)
+  --ignore-case         Ignore case of symbols
+  --include-dir dir     Set an include directory search path
+  --listing             Create a listing if assembly was ok
+  --list-bytes n        Maximum number of bytes per listing line
+  --macpack-dir dir     Set a macro package directory
+  --memory-model model  Set the memory model
+  --pagelength n        Set the page length for the listing
+  --smart               Enable smart mode
+  --target sys          Set the target system
+  --verbose             Increase verbosity
+  --version             Print the assembler version
+---------------------------------------------------------------------------
+
+
+

+ + +

2.2 Command line options in detail +

+ + +

Here is a description of all the command line options:

+

+

+

+

+
--cpu type
+

Set the default for the CPU type. The option takes a parameter, which +may be one of

+

6502, 65SC02, 65C02, 65816, sunplus, sweet16, HuC6280

+

The sunplus cpu is not available in the freeware version, because the +instruction set is "proprietary and confidential".

+ +

+

+
--feature name
+

Enable an emulation feature. This is identical as using .FEATURE +in the source with two exceptions: Feature names must be lower case, and +each feature must be specified by using an extra --feature option, +comma separated lists are not allowed.

+

See the discussion of the +.FEATURE +command for a list of emulation features.

+ + +
--forget-inc-paths
+

Forget the builtin include paths. This is most useful when building +customized assembler modules, in which case the standard header files should +be ignored.

+ +

+

+
-g, --debug-info
+

When this option (or the equivalent control command .DEBUGINFO) is +used, the assembler will add a section to the object file that contains +all symbols (including local ones) together with the symbol values and +source file positions. The linker will put these additional symbols into +the VICE label file, so even local symbols can be seen in the VICE +monitor.

+ + +
-h, --help
+

Print the short option summary shown above.

+ + +
-i, --ignore-case
+

This option makes the assembler case insensitive on identifiers and labels. +This option will override the default, but may itself be overridden by the + +.CASE control command.

+ + +
-l, --listing
+

Generate an assembler listing. The listing file will always have the +name of the main input file with the extension replaced by ".lst". This +may change in future versions.

+ + +
--list-bytes n
+

Set the maximum number of bytes printed in the listing for one line of +input. See the +.LISTBYTES directive +for more information. The value zero can be used to encode an unlimited +number of printed bytes.

+ + +
--macpack-dir dir
+

This options allows to specify a directory containing macro files that are +used instead of the builtin images when a +.MACPACK directive is encountered. If --macpack-dir +was specified, a .mac extension is added to the package name and +the resulting file is loaded from the given directory. This is most useful +when debugging the builtin macro packages.

+ + +
-mm model, --memory-model model
+

Define the default memory model. Possible model specifiers are near, far and +huge.

+ + +
-o name
+

The default output name is the name of the input file with the extension +replaced by ".o". If you don't like that, you may give another name with +the -o option. The output file will be placed in the same directory as +the source file, or, if -o is given, the full path in this name is used.

+ + +
--pagelength n
+

sets the length of a listing page in lines. See the +.PAGELENGTH directive for more information.

+ + +
-s, --smart-mode
+

In smart mode (enabled by -s or the +.SMART +pseudo instruction) the assembler will track usage of the REP and +SEP instructions in 65816 mode and update the operand sizes +accordingly. If the operand of such an instruction cannot be evaluated by +the assembler (for example, because the operand is an imported symbol), a +warning is issued.

+

Beware: Since the assembler cannot trace the execution flow this may +lead to false results in some cases. If in doubt, use the .ixx and .axx +instructions to tell the assembler about the current settings. Smart +mode is off by default.

+ +

+

+
-t sys, --target sys
+

Set the target system. This will enable translation of character strings +and character constants into the character set of the target platform. +The default for the target system is "none", which means that no translation +will take place. The assembler supports the same target systems as the +compiler, see there for a list.

+ + +
-v, --verbose
+

Increase the assembler verbosity. Usually only needed for debugging +purposes. You may use this option more than one time for even more +verbose output.

+ + +
-D
+

This option allows you to define symbols on the command line. Without a +value, the symbol is defined with the value zero. When giving a value, +you may use the '$' prefix for hexadecimal symbols. Please note +that for some operating systems, '$' has a special meaning, so +you may have to quote the expression.

+ + +
-I dir, --include-dir dir
+

Name a directory which is searched for include files. The option may be +used more than once to specify more than one directory to search. The +current directory is always searched first before considering any +additional directories. See also the section about +search paths.

+ + +
-U, --auto-import
+

Mark symbols that are not defined in the sources as imported symbols. This +should be used with care since it delays error messages about typos and such +until the linker is run. The compiler uses the equivalent of this switch +( +.AUTOIMPORT) to enable auto imported +symbols for the runtime library. However, the compiler is supposed to +generate code that runs through the assembler without problems, something +which is not always true for assembler programmers.

+ + +
-V, --version
+

Print the version number of the assembler. If you send any suggestions +or bugfixes, please include the version number.

+ +

+

+
-Wn
+

Set the warning level for the assembler. Using -W2 the assembler will +even warn about such things like unused imported symbols. The default +warning level is 1, and it would probably be silly to set it to +something lower.

+ +
+

+ + + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-3.html b/ca65-primer/doc/ca65-3.html new file mode 100755 index 0000000..292683d --- /dev/null +++ b/ca65-primer/doc/ca65-3.html @@ -0,0 +1,38 @@ + + + + + ca65 Users Guide: Search paths + + + + + +Next +Previous +Contents +
+

3. Search paths

+ + +

Include files are searched in the following places:

+

+

    +
  1. The current directory.
  2. +
  3. A compiled-in directory, which is often /usr/lib/cc65/asminc +on Linux systems.
  4. +
  5. The value of the environment variable CA65_INC if it is defined.
  6. +
  7. A subdirectory named asminc of the directory defined in the +environment variable CC65_HOME, if it is defined.
  8. +
  9. Any directory added with the -I option on the command line.
  10. +
+

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-4.html b/ca65-primer/doc/ca65-4.html new file mode 100755 index 0000000..bffcd5d --- /dev/null +++ b/ca65-primer/doc/ca65-4.html @@ -0,0 +1,181 @@ + + + + + ca65 Users Guide: Input format + + + + + +Next +Previous +Contents +
+

4. Input format

+ + + +

4.1 Assembler syntax +

+ + +

The assembler accepts the standard 6502/65816 assembler syntax. One line may +contain a label (which is identified by a colon), and, in addition to the +label, an assembler mnemonic, a macro, or a control command (see section +Control Commands for supported control +commands). Alternatively, the line may contain a symbol definition using +the '=' token. Everything after a semicolon is handled as a comment (that is, +it is ignored).

+

Here are some examples for valid input lines:

+

+

+
+        Label:                          ; A label and a comment
+                lda     #$20            ; A 6502 instruction plus comment
+        L1:     ldx     #$20            ; Same with label
+        L2:     .byte   "Hello world"   ; Label plus control command
+                mymac   $20             ; Macro expansion
+                MySym = 3*L1            ; Symbol definition
+        MaSym   = Label                 ; Another symbol
+
+
+

+

The assembler accepts

+

+

    +
  • all valid 6502 mnemonics when in 6502 mode (the default or after the + +.P02 command was given).
  • +
  • all valid 6502 mnemonics plus a set of illegal instructions when in +6502X mode.
  • +
  • all valid 65SC02 mnemonics when in 65SC02 mode (after the + +.PSC02 command was given).
  • +
  • all valid 65C02 mnemonics when in 65C02 mode (after the + +.PC02 command was given).
  • +
  • all valid 65618 mnemonics when in 65816 mode (after the + +.P816 command was given).
  • +
  • all valid SunPlus mnemonics when in SunPlus mode (after the + +.SUNPLUS command was given).
  • +
+

+ + +

4.2 65816 mode +

+ + +

In 65816 mode several aliases are accepted in addition to the official +mnemonics:

+

+

+
+        BGE is an alias for BCS
+        BLT is an alias for BCC
+        CPA is an alias for CMP
+        DEA is an alias for DEC A
+        INA is an alias for INC A
+        SWA is an alias for XBA
+        TAD is an alias for TCD
+        TAS is an alias for TCS
+        TDA is an alias for TDC
+        TSA is an alias for TSC
+
+
+

+ + + +

4.3 6502X mode +

+ + +

6502X mode is an extension to the normal 6502 mode. In this mode, several +mnemonics for illegal instructions of the NMOS 6502 CPUs are accepted. Since +these instructions are illegal, there are no official mnemonics for them. The +unofficial ones are taken from +http://oxyron.net/graham/opcodes02.html. Please note that only the +ones marked as "stable" are supported. The following table uses information +from the mentioned web page, for more information, see there.

+

+

    +
  • ALR: A:=(A and #{imm})*2;
  • +
  • ANC: A:=A and #{imm}; Generates opcode $0B.
  • +
  • ARR: A:=(A and #{imm})/2;
  • +
  • AXS: X:=A and X-#{imm};
  • +
  • DCP: {adr}:={adr}-1; A-{adr};
  • +
  • ISC: {adr}:={adr}+1; A:=A-{adr};
  • +
  • LAS: A,X,S:={adr} and S;
  • +
  • LAX: A,X:={adr};
  • +
  • RLA: {adr}:={adr}rol; A:=A and {adr};
  • +
  • RRA: {adr}:={adr}ror; A:=A adc {adr};
  • +
  • SAX: {adr}:=A and X;
  • +
  • SLO: {adr}:={adr}*2; A:=A or {adr};
  • +
  • SRE: {adr}:={adr}/2; A:=A xor {adr};
  • +
+

+ + + +

4.4 sweet16 mode +

+ + +

SWEET 16 is an interpreter for a pseudo 16 bit CPU written by Steve Wozniak +for the Apple ][ machines. It is available in the Apple ][ ROM. ca65 can +generate code for this pseudo CPU when switched into sweet16 mode. The +following is special in sweet16 mode:

+

+

    +
  • The '@' character denotes indirect addressing and is no longer available +for cheap local labels. If you need cheap local labels, you will have to +switch to another lead character using the +.LOCALCHAR command. +
  • +
  • Registers are specified using R0 .. R15. In sweet16 mode, +these identifiers are reserved words. +
  • +
+

+

Please note that the assembler does neither supply the interpreter needed for +SWEET 16 code, nor the zero page locations needed for the SWEET 16 registers, +nor does it call the interpreter. All this must be done by your program. Apple +][ programmers do probably know how to use sweet16 mode.

+

For more information about SWEET 16, see +http://www.6502.org/source/interpreters/sweet16.htm.

+ + +

4.5 Number format +

+ + +

For literal values, the assembler accepts the widely used number formats: A +preceding '$' or a trailing 'h' denotes a hex value, a preceding '%' +denotes a binary value, and a bare number is interpreted as a decimal. There +are currently no octal values and no floats.

+ + +

4.6 Conditional assembly +

+ + +

Please note that when using the conditional directives (.IF and friends), +the input must consist of valid assembler tokens, even in .IF branches +that are not assembled. The reason for this behaviour is that the assembler +must still be able to detect the ending tokens (like .ENDIF), so +conversion of the input stream into tokens still takes place. As a consequence +conditional assembly directives may not be used to prevent normal text +(used as a comment or similar) from being assembled.

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-5.html b/ca65-primer/doc/ca65-5.html new file mode 100755 index 0000000..7d13726 --- /dev/null +++ b/ca65-primer/doc/ca65-5.html @@ -0,0 +1,155 @@ + + + + + ca65 Users Guide: Expressions + + + + + +Next +Previous +Contents +
+

5. Expressions

+ + + + +

5.1 Expression evaluation +

+ + +

All expressions are evaluated with (at least) 32 bit precision. An +expression may contain constant values and any combination of internal and +external symbols. Expressions that cannot be evaluated at assembly time +are stored inside the object file for evaluation by the linker. +Expressions referencing imported symbols must always be evaluated by the +linker.

+ + +

5.2 Size of an expression result +

+ + +

Sometimes, the assembler must know about the size of the value that is the +result of an expression. This is usually the case, if a decision has to be +made, to generate a zero page or an absolute memory references. In this +case, the assembler has to make some assumptions about the result of an +expression:

+

+

    +
  • If the result of an expression is constant, the actual value is +checked to see if it's a byte sized expression or not.
  • +
  • If the expression is explicitly casted to a byte sized expression by +one of the '>', '<' or '^' operators, it is a byte expression.
  • +
  • If this is not the case, and the expression contains a symbol, +explicitly declared as zero page symbol (by one of the .importzp or +.exportzp instructions), then the whole expression is assumed to be +byte sized.
  • +
  • If the expression contains symbols that are not defined, and these +symbols are local symbols, the enclosing scopes are searched for a +symbol with the same name. If one exists and this symbol is defined, +it's attributes are used to determine the result size.
  • +
  • In all other cases the expression is assumed to be word sized.
  • +
+

+

Note: If the assembler is not able to evaluate the expression at assembly +time, the linker will evaluate it and check for range errors as soon as +the result is known.

+ + +

5.3 Boolean expressions +

+ + +

In the context of a boolean expression, any non zero value is evaluated as +true, any other value to false. The result of a boolean expression is 1 if +it's true, and zero if it's false. There are boolean operators with extreme +low precedence with version 2.x (where x > 0). The .AND and .OR +operators are shortcut operators. That is, if the result of the expression is +already known, after evaluating the left hand side, the right hand side is +not evaluated.

+ + +

5.4 Constant expressions +

+ + +

Sometimes an expression must evaluate to a constant without looking at any +further input. One such example is the +.IF command +that decides if parts of the code are assembled or not. An expression used in +the .IF command cannot reference a symbol defined later, because the +decision about the .IF must be made at the point when it is read. If the +expression used in such a context contains only constant numerical values, +there is no problem. When unresolvable symbols are involved it may get harder +for the assembler to determine if the expression is actually constant, and it +is even possible to create expressions that aren't recognized as constant. +Simplifying the expressions will often help.

+

In cases where the result of the expression is not needed immediately, the +assembler will delay evaluation until all input is read, at which point all +symbols are known. So using arbitrary complex constant expressions is no +problem in most cases.

+ + + +

5.5 Available operators +

+ + +

+

+
+Operator Description Precedence
+ Built-in string functions 0
+ 
+ Built-in pseudo-variables 1
+ Built-in pseudo-functions 1
++ Unary positive 1
+- Unary negative 1
+~
.BITNOT
Unary bitwise not 1
+<
.LOBYTE
Unary low-byte operator 1
+>
.HIBYTE
Unary high-byte operator 1
+^
.BANKBYTE
Unary bank-byte operator 1
+ 
+* Multiplication 2
+/ Division 2
+.MOD Modulo operator 2
+&
.BITAND
Bitwise and 2
+^
.BITXOR
Binary bitwise xor 2
+<<
.SHL
Shift-left operator 2
+>>
.SHR
Shift-right operator 2
+ 
++ Binary addition 3
+- Binary subtraction 3
+|
.BITOR
Bitwise or 3
+ 
+= Compare operator (equal) 4
+<> Compare operator (not equal) 4
+< Compare operator (less) 4
+> Compare operator (greater) 4
+<= Compare operator (less or equal) 4
+>= Compare operator (greater or equal) 4
+ 
+&&
.AND
Boolean and 5
+.XOR Boolean xor 5
+ 
+||
.OR
Boolean or 6
+ 
+!
.NOT
Boolean not 7
+
+Available operators, sorted by precedence +

+

+

To force a specific order of evaluation, parentheses may be used, as usual.

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-6.html b/ca65-primer/doc/ca65-6.html new file mode 100755 index 0000000..a80054a --- /dev/null +++ b/ca65-primer/doc/ca65-6.html @@ -0,0 +1,212 @@ + + + + + ca65 Users Guide: Symbols and labels + + + + + +Next +Previous +Contents +
+

6. Symbols and labels

+ + +

A symbol or label is an identifier that starts with a letter and is followed +by letters and digits. Depending on some features enabled (see + +at_in_identifiers, + +dollar_in_identifiers and + +leading_dot_in_identifiers) +other characters may be present. Use of identifiers consisting of a single +character will not work in all cases, because some of these identifiers are +reserved keywords (for example "A" is not a valid identifier for a label, +because it is the keyword for the accumulator).

+

The assembler allows you to use symbols instead of naked values to make +the source more readable. There are a lot of different ways to define and +use symbols and labels, giving a lot of flexibility.

+ +

6.1 Numeric constants +

+ + +

Numeric constants are defined using the equal sign or the label assignment +operator. After doing

+

+

+
+      two = 2
+
+
+

+

may use the symbol "two" in every place where a number is expected, and it is +evaluated to the value 2 in this context. The label assignment operator causes +the same, but causes the symbol to be marked as a label, which may cause a +different handling in the debugger:

+

+

+
+      io := $d000
+
+
+

+

The right side can of course be an expression:

+

+

+
+      four = two * two
+
+
+

+ + +

6.2 Standard labels +

+ + +

A label is defined by writing the name of the label at the start of the line +(before any instruction mnemonic, macro or pseudo directive), followed by a +colon. This will declare a symbol with the given name and the value of the +current program counter.

+ + +

6.3 Local labels and symbols +

+ + +

Using the +.PROC directive, it is possible to +create regions of code where the names of labels and symbols are local to this +region. They are not known outside of this region and cannot be accessed from +there. Such regions may be nested like PROCEDUREs in Pascal.

+

See the description of the +.PROC +directive for more information.

+ + +

6.4 Cheap local labels +

+ + +

Cheap local labels are defined like standard labels, but the name of the +label must begin with a special symbol (usually '@', but this can be +changed by the +.LOCALCHAR +directive).

+

Cheap local labels are visible only between two non cheap labels. As soon as a +standard symbol is encountered (this may also be a local symbol if inside a +region defined with the +.PROC directive), the +cheap local symbol goes out of scope.

+

You may use cheap local labels as an easy way to reuse common label +names like "Loop". Here is an example:

+

+

+
+        Clear:  lda    #$00             ; Global label
+                ldy    #$20
+        @Loop:  sta    Mem,y            ; Local label
+                dey
+                bne    @Loop            ; Ok
+                rts
+        Sub:    ...                     ; New global label
+                bne    @Loop            ; ERROR: Unknown identifier!
+
+
+

+ +

6.5 Unnamed labels +

+ + +

If you really want to write messy code, there are also unnamed labels. These +labels do not have a name (you guessed that already, didn't you?). A colon is +used to mark the absence of the name.

+

Unnamed labels may be accessed by using the colon plus several minus or plus +characters as a label designator. Using the '-' characters will create a back +reference (use the n'th label backwards), using '+' will create a forward +reference (use the n'th label in forward direction). An example will help to +understand this:

+

+

+
+        :       lda     (ptr1),y        ; #1
+                cmp     (ptr2),y
+                bne     :+              ; -> #2
+                tax
+                beq     :+++            ; -> #4
+                iny
+                bne     :-              ; -> #1
+                inc     ptr1+1
+                inc     ptr2+1
+                bne     :-              ; -> #1
+
+        :       bcs     :+              ; #2 -> #3
+                ldx     #$FF
+                rts
+
+        :       ldx     #$01            ; #3
+        :       rts                     ; #4
+
+
+

+

As you can see from the example, unnamed labels will make even short +sections of code hard to understand, because you have to count labels +to find branch targets (this is the reason why I for my part do +prefer the "cheap" local labels). Nevertheless, unnamed labels are +convenient in some situations, so it's your decision.

+ + +

6.6 Using macros to define labels and constants +

+ + +

While there are drawbacks with this approach, it may be handy in some +situations. Using +.DEFINE, it is +possible to define symbols or constants that may be used elsewhere. Since +the macro facility works on a very low level, there is no scoping. On the +other side, you may also define string constants this way (this is not +possible with the other symbol types).

+

Example:

+

+

+
+        .DEFINE two     2
+        .DEFINE version "SOS V2.3"
+
+        four = two * two        ; Ok
+        .byte   version         ; Ok
+
+        .PROC                   ; Start local scope
+        two = 3                 ; Will give "2 = 3" - invalid!
+        .ENDPROC
+
+
+

+ + +

6.7 Symbols and .DEBUGINFO +

+ + +

If +.DEBUGINFO is enabled (or +-g is given on the command line), global, local and +cheap local labels are written to the object file and will be available in the +symbol file via the linker. Unnamed labels are not written to the object file, +because they don't have a name which would allow to access them.

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-7.html b/ca65-primer/doc/ca65-7.html new file mode 100755 index 0000000..0886b03 --- /dev/null +++ b/ca65-primer/doc/ca65-7.html @@ -0,0 +1,322 @@ + + + + + ca65 Users Guide: Scopes + + + + + +Next +Previous +Contents +
+

7. Scopes

+ + +

ca65 implements several sorts of scopes for symbols.

+ +

7.1 Global scope +

+ + +

All (non cheap local) symbols that are declared outside of any nested scopes +are in global scope.

+ + +

7.2 Cheap locals +

+ + +

A special scope is the scope for cheap local symbols. It lasts from one non +local symbol to the next one, without any provisions made by the programmer. +All other scopes differ in usage but use the same concept internally.

+ + +

7.3 Generic nested scopes +

+ + +

A nested scoped for generic use is started with +.SCOPE and closed with +.ENDSCOPE. +The scope can have a name, in which case it is accessible from the outside by +using +explicit scopes. If the scope does not +have a name, all symbols created within the scope are local to the scope, and +aren't accessible from the outside.

+

A nested scope can access symbols from the local or from enclosing scopes by +name without using explicit scope names. In some cases there may be +ambiguities, for example if there is a reference to a local symbol that is not +yet defined, but a symbol with the same name exists in outer scopes:

+

+

+
+        .scope  outer
+                foo     = 2
+                .scope  inner
+                        lda     #foo
+                        foo     = 3
+                .endscope
+        .endscope
+
+
+

+

In the example above, the lda instruction will load the value 3 into the +accumulator, because foo is redefined in the scope. However:

+

+

+
+        .scope  outer
+                foo     = $1234
+                .scope  inner
+                        lda     foo,x
+                        foo     = $12
+                .endscope
+        .endscope
+
+
+

+

Here, lda will still load from $12,x, but since it is unknown to the +assembler that foo is a zeropage symbol when translating the instruction, +absolute mode is used instead. In fact, the assembler will not use absolute +mode by default, but it will search through the enclosing scopes for a symbol +with the given name. If one is found, the address size of this symbol is used. +This may lead to errors:

+

+

+
+        .scope  outer
+                foo     = $12
+                .scope  inner
+                        lda     foo,x
+                        foo     = $1234
+                .endscope
+        .endscope
+
+
+

+

In this case, when the assembler sees the symbol foo in the lda +instruction, it will search for an already defined symbol foo. It will +find foo in scope outer, and a close look reveals that it is a +zeropage symbol. So the assembler will use zeropage addressing mode. If +foo is redefined later in scope inner, the assembler tries to change +the address in the lda instruction already translated, but since the new +value needs absolute addressing mode, this fails, and an error message "Range +error" is output.

+

Of course the most simple solution for the problem is to move the definition +of foo in scope inner upwards, so it precedes its use. There may be +rare cases when this cannot be done. In these cases, you can use one of the +address size override operators:

+

+

+
+        .scope  outer
+                foo     = $12
+                .scope  inner
+                        lda     a:foo,x
+                        foo     = $1234
+                .endscope
+        .endscope
+
+
+

+

This will cause the lda instruction to be translated using absolute +addressing mode, which means changing the symbol reference later does not +cause any errors.

+ + +

7.4 Nested procedures +

+ + +

A nested procedure is created by use of +.PROC. It +differs from a +.SCOPE in that it must have a +name, and a it will introduce a symbol with this name in the enclosing scope. +So

+

+

+
+        .proc   foo
+                ...
+        .endscope
+
+
+

+

is actually the same as

+

+

+
+        foo:
+        .scope  foo
+                ...
+        .endscope
+
+
+

+

This is the reason why a procedure must have a name. If you want a scope +without a name, use +.SCOPE.

+

Note: As you can see from the example above, scopes and symbols live in +different namespaces. There can be a symbol named foo and a scope named +foo without any conflicts (but see the section titled +"Scope search order").

+ + +

7.5 Structs, unions and enums +

+ + +

Structs, unions and enums are explained in a +separate section, I do only cover them here, because if they are declared with a +name, they open a nested scope, similar to +.SCOPE. However, when no name is specified, the behaviour is +different: In this case, no new scope will be opened, symbols declared within +a struct, union, or enum declaration will then be added to the enclosing scope +instead.

+ + +

7.6 Explicit scope specification +

+ + +

Accessing symbols from other scopes is possible by using an explicit scope +specification, provided that the scope where the symbol lives in has a name. +The namespace token (::) is used to access other scopes:

+

+

+
+        .scope  foo
+        bar:    .word   0
+        .endscope
+
+                ...
+                lda     foo::bar        ; Access foo in scope bar
+
+
+

+

The only way to deny access to a scope from the outside is to declare a scope +without a name (using the +.SCOPE command).

+

A special syntax is used to specify the global scope: If a symbol or scope is +preceded by the namespace token, the global scope is searched:

+

+

+
+        bar     = 3
+
+        .scope  foo
+                bar     = 2
+                lda     #::bar  ; Access the global bar (which is 3)
+        .endscope
+
+
+

+ + +

7.7 Scope search order +

+ + +

The assembler searches for a scope in a similar way as for a symbol. First, it +looks in the current scope, and then it walks up the enclosing scopes until +the scope is found.

+

However, one important thing to note when using explicit scope syntax is, that +a symbol may be accessed before it is defined, but a scope may not be +used without a preceding definition. This means that in the following +example:

+

+

+
+        .scope  foo
+                bar     = 3
+        .endscope
+
+        .scope  outer
+                lda     #foo::bar  ; Will load 3, not 2!
+                .scope  foo
+                        bar     = 2
+                .endscope
+        .endscope
+
+
+

+

the reference to the scope foo will use the global scope, and not the +local one, because the local one is not visible at the point where it is +referenced.

+

Things get more complex if a complete chain of scopes is specified:

+

+

+
+        .scope  foo
+                .scope  outer
+                        .scope  inner
+                                bar = 1
+                        .endscope
+                .endscope
+                .scope  another
+                        .scope  nested
+                                lda     #outer::inner::bar      ; 1
+                        .endscope
+                .endscope
+        .endscope
+
+        .scope  outer
+                .scope  inner
+                        bar = 2
+                .endscope
+        .endscope
+
+
+

+

When outer::inner::bar is referenced in the lda instruction, the +assembler will first search in the local scope for a scope named outer. +Since none is found, the enclosing scope (another) is checked. There is +still no scope named outer, so scope foo is checked, and finally +scope outer is found. Within this scope, inner is searched, and in +this scope, the assembler looks for a symbol named bar.

+

Please note that once the anchor scope is found, all following scopes +(inner in this case) are expected to be found exactly in this scope. The +assembler will search the scope tree only for the first scope (if it is not +anchored in the root scope). Starting from there on, there is no flexibility, +so if the scope named outer found by the assembler does not contain a +scope named inner, this would be an error, even if such a pair does exist +(one level up in global scope).

+

Ambiguities that may be introduced by this search algorithm may be removed by +anchoring the scope specification in the global scope. In the example above, +if you want to access the "other" symbol bar, you would have to write:

+

+

+
+        .scope  foo
+                .scope  outer
+                        .scope  inner
+                                bar = 1
+                        .endscope
+                .endscope
+                .scope  another
+                        .scope  nested
+                                lda     #::outer::inner::bar    ; 2
+                        .endscope
+                .endscope
+        .endscope
+
+        .scope  outer
+                .scope  inner
+                        bar = 2
+                .endscope
+        .endscope
+
+
+

+ + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-8.html b/ca65-primer/doc/ca65-8.html new file mode 100755 index 0000000..a7faaf8 --- /dev/null +++ b/ca65-primer/doc/ca65-8.html @@ -0,0 +1,86 @@ + + + + + ca65 Users Guide: Address sizes and memory models + + + + + +Next +Previous +Contents +
+

8. Address sizes and memory models

+ + + +

8.1 Address sizes +

+ + +

ca65 assigns each segment and each symbol an address size. This is true, even +if the symbol is not used as an address. You may also think of a value range +of the symbol instead of an address size.

+

Possible address sizes are:

+

+

    +
  • Zeropage or direct (8 bits)
  • +
  • Absolute (16 bits)
  • +
  • Far (24 bits)
  • +
  • Long (32 bits)
  • +
+

+

Since the assembler uses default address sizes for the segments and symbols, +it is usually not necessary to override the default behaviour. In cases, where +it is necessary, the following keywords may be used to specify address sizes:

+

+

    +
  • DIRECT, ZEROPAGE or ZP for zeropage addressing (8 bits).
  • +
  • ABSOLUTE, ABS or NEAR for absolute addressing (16 bits).
  • +
  • FAR for far addressing (24 bits).
  • +
  • LONG or DWORD for long addressing (32 bits).
  • +
+

+ + +

8.2 Address sizes of segments +

+ + +

The assembler assigns an address size to each segment. Since the +representation of a label within this segment is "segment start + offset", +labels will inherit the address size of the segment they are declared in.

+

The address size of a segment may be changed, by using an optional address +size modifier. See the +segment directive for +an explanation on how this is done.

+ + +

8.3 Address sizes of symbols +

+ + + + + + +

8.4 Memory models +

+ + +

The default address size of a segment depends on the memory model used. Since +labels inherit the address size from the segment they are declared in, +changing the memory model is an easy way to change the address size of many +symbols at once.

+ + + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65-9.html b/ca65-primer/doc/ca65-9.html new file mode 100755 index 0000000..18ba0df --- /dev/null +++ b/ca65-primer/doc/ca65-9.html @@ -0,0 +1,129 @@ + + + + + ca65 Users Guide: Pseudo variables + + + + + +Next +Previous +Contents +
+

9. Pseudo variables

+ + +

Pseudo variables are readable in all cases, and in some special cases also +writable.

+ +

9.1 * +

+ + +

Reading this pseudo variable will return the program counter at the start +of the current input line.

+

Assignment to this variable is possible when +.FEATURE pc_assignment is used. Note: You should not use +assignments to *, use +.ORG instead.

+ + +

9.2 .CPU +

+ + +

Reading this pseudo variable will give a constant integer value that +tells which CPU is currently enabled. It can also tell which instruction +set the CPU is able to translate. The value read from the pseudo variable +should be further examined by using one of the constants defined by the +"cpu" macro package (see +.MACPACK).

+

It may be used to replace the .IFPxx pseudo instructions or to construct +even more complex expressions.

+

Example:

+

+

+
+        .macpack        cpu
+        .if     (.cpu .bitand CPU_ISET_65816)
+                phx
+                phy
+        .else
+                txa
+                pha
+                tya
+                pha
+        .endif
+  
+
+
+

+ + +

9.3 .PARAMCOUNT +

+ + +

This builtin pseudo variable is only available in macros. It is replaced by +the actual number of parameters that were given in the macro invocation.

+

Example:

+

+

+
+        .macro  foo     arg1, arg2, arg3
+        .if     .paramcount <> 3
+        .error  "Too few parameters for macro foo"
+        .endif
+        ...
+        .endmacro
+  
+
+
+

+

See section +Macros.

+ + +

9.4 .TIME +

+ + +

Reading this pseudo variable will give a constant integer value that +represents the current time in POSIX standard (as seconds since the +Epoch).

+

It may be used to encode the time of translation somewhere in the created +code.

+

Example:

+

+

+
+        .dword  .time   ; Place time here
+  
+
+
+

+ + +

9.5 .VERSION +

+ + +

Reading this pseudo variable will give the assembler version according to +the following formula:

+

VER_MAJOR*$100 + VER_MINOR*$10 + VER_PATCH

+

It may be used to encode the assembler version or check the assembler for +special features not available with older versions.

+

Example:

+

Version 2.11.1 of the assembler will return $2B1 as numerical constant when +reading the pseudo variable .VERSION.

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65.html b/ca65-primer/doc/ca65.html new file mode 100755 index 0000000..0965e64 --- /dev/null +++ b/ca65-primer/doc/ca65.html @@ -0,0 +1,289 @@ + + + + + ca65 Users Guide + + + + + +Next +Previous +Contents +
+

ca65 Users Guide

+ +

Ullrich von Bassewitz, +uz@cc65.org

2000-07-19, 2000-11-29, 2001-10-02, 2005-09-08 +
+ca65 is a powerful macro assembler for the 6502, 65C02 and 65816 CPUs. It is +used as a companion assembler for the cc65 crosscompiler, but it may also be +used as a standalone product. +
+

+

1. Overview

+ + +

+

2. Usage

+ + +

+

3. Search paths

+ +

+

4. Input format

+ + +

+

5. Expressions

+ + +

+

6. Symbols and labels

+ + +

+

7. Scopes

+ + +

+

8. Address sizes and memory models

+ + +

+

9. Pseudo variables

+ + +

+

10. Pseudo functions

+ + +

+

11. Control commands

+ + +

+

12. Macros

+ + +

+

13. Macro packages

+ + +

+

14. Predefined constants

+ +

+

15. Structs and unions

+ + +

+

16. Module constructors/destructors

+ + +

+

17. Porting sources from other assemblers

+ + +

+

18. Bugs/Feedback

+ +

+

19. Copyright

+ +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65html-1.html b/ca65-primer/doc/ca65html-1.html new file mode 100755 index 0000000..3408099 --- /dev/null +++ b/ca65-primer/doc/ca65html-1.html @@ -0,0 +1,30 @@ + + + + + ca65html Users Guide: Overview + + + + + +Next +Previous +Contents +
+

1. Overview

+ + +

ca65html converts assembly source files written for use with the +ca65 crossassembler into HTML. It is a standalone +tool written in PERL; and as such, it does not understand the structure of +assembler sources in the same depth as ca65 does, so it may fail in very rare +cases. In all other cases, it generates very nice output.

+ + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65html-2.html b/ca65-primer/doc/ca65html-2.html new file mode 100755 index 0000000..c1dd9be --- /dev/null +++ b/ca65-primer/doc/ca65html-2.html @@ -0,0 +1,191 @@ + + + + + ca65html Users Guide: Usage + + + + + +Next +Previous +Contents +
+

2. Usage

+ + + + +

2.1 Command line option overview +

+ + +

The HTML converter accepts the following options:

+

+

+
+---------------------------------------------------------------------------
+Usage: ca65html [options] file ...
+Options:
+  --bgcolor c        Use background color c instead of #FFFFFF
+  --colorize         Add color highlights to the output
+  --commentcolor c   Use color c for comments instead of #B22222
+  --crefs            Generate references to the C source file(s)
+  --ctrlcolor c      Use color c for directives instead of #228B22
+  --cvttabs          Convert tabs to spaces in the output
+  --help             This text
+  --htmldir dir      Specify directory for HTML files
+  --indexcols n      Use n columns on index page (default 6)
+  --indexname file   Use file for the index file instead of index.html
+  --indexpage        Create an index page
+  --indextitle title Use title as the index title instead of Index
+  --keywordcolor c   Use color c for keywords instead of #A020F0
+  --linelabels       Generate a linexxx HTML label for each line
+  --linenumbers      Add line numbers to the output
+  --linkstyle style  Use the given link style
+  --replaceext       Replace source extension instead of appending .html
+  --textcolor c      Use text color c instead of #000000
+  --verbose          Be more verbose
+---------------------------------------------------------------------------
+
+
+

+ + +

2.2 Command line options in detail +

+ + +

Here is a description of all the command line options:

+

+

+ +
--bgcolor c
+

Set the background color. The argument c must be a valid HTML color, usually +given as RGB triplet in the form #rrggbb, where r, g, and b are the +respective red, green, and blue parts as two-digit hex values. The default is +#FFFFFF (white). That color is used in the <body> of the +generated HTML output.

+ + +
--colorize
+

Colorize the output. The converter outputs processor instructions, assembler +control commands, and comments in different colors.

+ + +
--commentcolor c
+

Set the color used for comments. The argument c must be a valid HTML color, +usually given as RGB triplet in the form #rrggbb, where r, g, and b are +the respective red, green, and blue parts as two-digit hex values. The +default is #B22222 (red).

+

Note that this option has no effect if --colorize is not also given.

+ + +
--crefs
+

Generate references to the C file, when a .dbg command is found with a +file name. The converter assumes that the C source was also converted into +HTML (for example by use of c2html), has the name file.c.html, and +lives in the same directory as the assembler file. If the .dbg +directive specifies a line, a link to the correct line in the C file is +generated, using a label in the form linexxx, as it is created by +c2html by use of the -n option.

+ + +
--commentcolor c
+

Set the color used for assembler control commands. The argument c must be a +valid HTML color, usually given as RGB triplet in the form #rrggbb, +where r, g, and b are the respective red, green, and blue parts as two-digit +hex values. The default is #228B22 (green).

+

Note that this option has no effect if --colorize is not also given.

+ + +
--cvttabs
+

Convert tabs in the input into spaces in the output, assuming the standard +tab width of 8. This is useful if the --linenumbers option is used to +retain the indentation.

+ + +
--help
+

Print the command line option summary shown above.

+ + +
--htmldir dir
+

Specify an output directory for the generated HTML files.

+ + +
--indexcols n
+

Use n columns on the index page. This option has no effect if used without +--indexpage.

+ + +
--indexname name
+

Use another index file name instead of index.html. This option has no +effect if used without --indexpage.

+ + +
--indexpage
+

Causes the converter to generate an index page listing file names, and all +exports found in the converted files.

+ + +
--indextitle title
+

Use "title" as the title of the index page. This option has no effect if +used without --indexpage.

+ + +
--keywordcolor c
+

Set the color used for processor instructions. The argument c must be a +valid HTML color, usually given as RGB triplet in the form #rrggbb, +where r, g, and b are the respective red, green, and blue parts as two-digit +hex values. The default is #A020F0 (purple).

+

Note that this option has no effect if --colorize is not also given.

+ +
--linelabels
+

Generate a label for each line using the name linexxx where xxx is the +number of the line.

+

Note: The converter will not make use of this label. Use this option if you +have other HTML pages referencing the converted assembler file.

+ + +
--linenumbers
+

Generate line numbers on the left side of the output.

+ + +
--linkstyle n
+

Influences the style used when generating links for imports. If n is zero +(the default), the converter creates a link to the actual symbol if it is +defined somewhere in the input files. If not, it creates a link to the +.import statement. If n is one, the converter will always generate a +HTML link to .import statement.

+ + +
--replaceext
+

Replace the file extension of the input file instead of appending .html +when generating the output file name.

+ + +
--textcolor c
+

Set the color for normal text. The argument c must be a valid HTML color, +usually given as RGB triplet in the form #rrggbb, where r, g, and b are +the respective red, green, and blue parts as two-digit hex values. The +default is #000000 (black). This color is used in the <body> +of the generated HTML output.

+ + +
--verbose
+

Increase the converter verbosity. Without this option, ca65html is quiet +when working. If you have a slow machine and lots of files to convert, you +might like a little bit more progress information.

+ +
+

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65html-3.html b/ca65-primer/doc/ca65html-3.html new file mode 100755 index 0000000..a61f659 --- /dev/null +++ b/ca65-primer/doc/ca65html-3.html @@ -0,0 +1,70 @@ + + + + + ca65html Users Guide: Peculiarities + + + + + +Next +Previous +Contents +
+

3. Peculiarities

+ + + +

3.1 Cross links +

+ + +

Since ca65html is able to generate links between modules, the best way to use +it is to supply all modules to it in one run, instead of running each file +separately through it.

+ + +

3.2 Include files +

+ + +

For now, ca65html will not read files included with .include. Specifying +the include files as normal input files on the command line works in many +cases.

+ + +

3.3 Conversion errors +

+ + +

Since ca65html does not really parse the input, but does most of its work +applying text patterns, it doesn't know anything about scoping and advanced +features of the assembler. This means that it might miss a label. And, it +might choose the wrong color for an item, in rare cases. Because it's just a +tool for displaying sources in a nice form, I think that's OK. Anyway, if you +find a conversion problem, you can send me a short piece of example input code. +If possible, I will fix it.

+ + +

3.4 Colorization +

+ + +

While having colors in the output looks really nice, it has one drawback:

+

+

    +
  1. Because lots of <span> tags are created in the output, +the size of the output file literally will explode. It seems to be the price +that you have to pay for color. +
  2. +
+

+ + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65html-4.html b/ca65-primer/doc/ca65html-4.html new file mode 100755 index 0000000..a68f5ba --- /dev/null +++ b/ca65-primer/doc/ca65html-4.html @@ -0,0 +1,30 @@ + + + + + ca65html Users Guide: Bugs/Feedback + + + + + +Next +Previous +Contents +
+

4. Bugs/Feedback

+ + +

If you have problems using the converter, if you find any bugs, or if you're +doing something interesting with the assembler, I would be glad to hear from +you. Feel free to contact me by email ( +uz@cc65.org).

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65html-5.html b/ca65-primer/doc/ca65html-5.html new file mode 100755 index 0000000..8270152 --- /dev/null +++ b/ca65-primer/doc/ca65html-5.html @@ -0,0 +1,45 @@ + + + + + ca65html Users Guide: Copyright + + + + +Next +Previous +Contents +
+

5. Copyright

+ + +

ca65html is (c) Copyright 2000-2007 Ullrich von Bassewitz. For its use, the +following conditions apply:

+

This software is provided 'as-is', without any expressed or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software.

+

Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions:

+

+

    +
  1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required.
  2. +
  3. Altered source versions must be plainly marked as such, and must not +be misrepresented as being the original software.
  4. +
  5. This notice may not be removed or altered from any source +distribution.
  6. +
+

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ca65html.html b/ca65-primer/doc/ca65html.html new file mode 100755 index 0000000..39b0297 --- /dev/null +++ b/ca65-primer/doc/ca65html.html @@ -0,0 +1,53 @@ + + + + + ca65html Users Guide + + + + + +Next +Previous +Contents +
+

ca65html Users Guide

+ +

Ullrich von Bassewitz, +uz@cc65.org

2007-10-2 +
+ca65html is an assembly-source-to-HTML converter. It is very useful if you +want to publish your assembler sources in the web. +
+

+

1. Overview

+ +

+

2. Usage

+ + +

+

3. Peculiarities

+ + +

+

4. Bugs/Feedback

+ +

+

5. Copyright

+ +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ld65-1.html b/ca65-primer/doc/ld65-1.html new file mode 100755 index 0000000..daf811b --- /dev/null +++ b/ca65-primer/doc/ld65-1.html @@ -0,0 +1,50 @@ + + + + + ld65 Users Guide: Overview + + + + + +Next +Previous +Contents +
+

1. Overview

+ + +

The ld65 linker combines several object modules created by the ca65 +assembler, producing an executable file. The object modules may be read +from a library created by the ar65 archiver (this is somewhat faster and +more convenient). The linker was designed to be as flexible as possible. +It complements the features that are built into the ca65 macroassembler:

+

+

    +
  • Accept any number of segments to form an executable module. +
  • +
  • Resolve arbitrary expressions stored in the object files. +
  • +
  • In case of errors, use the meta information stored in the object files +to produce helpful error messages. In case of undefined symbols, +expression range errors, or symbol type mismatches, ld65 is able to +tell you the exact location in the original assembler source, where +the symbol was referenced. +
  • +
  • Flexible output. The output of ld65 is highly configurable by a config +file. More common platforms are supported by builtin configurations +that may be activated by naming the target system. The output +generation was designed with different output formats in mind, so +adding other formats shouldn't be a great problem. +
  • +
+

+ + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ld65-2.html b/ca65-primer/doc/ld65-2.html new file mode 100755 index 0000000..23a6edc --- /dev/null +++ b/ca65-primer/doc/ld65-2.html @@ -0,0 +1,275 @@ + + + + + ld65 Users Guide: Usage + + + + + +Next +Previous +Contents +
+

2. Usage

+ + + + +

2.1 Command line option overview +

+ + +

The linker is called as follows:

+

+

+
+---------------------------------------------------------------------------
+Usage: ld65 [options] module ...
+Short options:
+  -(                    Start a library group
+  -)                    End a library group
+  -C name               Use linker config file
+  -D sym=val            Define a symbol
+  -L path               Specify a library search path
+  -Ln name              Create a VICE label file
+  -S addr               Set the default start address
+  -V                    Print the linker version
+  -h                    Help (this text)
+  -m name               Create a map file
+  -o name               Name the default output file
+  -t sys                Set the target system
+  -u sym                Force an import of symbol `sym'
+  -v                    Verbose mode
+  -vm                   Verbose map file
+
+Long options:
+  --cfg-path path       Specify a config file search path
+  --config name         Use linker config file
+  --dbgfile name        Generate debug information
+  --define sym=val      Define a symbol
+  --dump-config name    Dump a builtin configuration
+  --end-group           End a library group
+  --force-import sym    Force an import of symbol `sym'
+  --help                Help (this text)
+  --lib file            Link this library
+  --lib-path path       Specify a library search path
+  --mapfile name        Create a map file
+  --module-id id        Specify a module id
+  --obj file            Link this object file
+  --obj-path path       Specify an object file search path
+  --start-addr addr     Set the default start address
+  --start-group         Start a library group
+  --target sys          Set the target system
+  --version             Print the linker version
+---------------------------------------------------------------------------
+
+
+

+ + +

2.2 Command line options in detail +

+ + +

Here is a description of all the command line options:

+

+

+

+

+
-(, --start-group
+

Start a library group. The libraries specified within a group are searched +multiple times to resolve crossreferences within the libraries. Normally, +crossreferences are only resolved within a library, that is the library is +searched multiple times. Libraries specified later on the command line +cannot reference otherwise unreferenced symbols in libraries specified +earlier, because the linker has already handled them. Library groups are +a solution for this problem, because the linker will search repeatedly +through all libraries specified in the group, until all possible open +symbol references have been satisfied.

+ + +
-), --end-group
+

End a library group. See the explanation of the +--start-group option.

+ + +
-h, --help
+

Print the short option summary shown above.

+ +

+

+
-m name, --mapfile name
+

This option (which needs an argument that will used as a filename for +the generated map file) will cause the linker to generate a map file. +The map file does contain a detailed overview over the modules used, the +sizes for the different segments, and a table containing exported +symbols.

+ +

+

+
-o name
+

The -o switch is used to give the name of the default output file. +Depending on your output configuration, this name may NOT be used as +name for the output file. However, for the builtin configurations, this +name is used for the output file name.

+ +

+

+
-t sys, --target sys
+

The argument for the -t switch is the name of the target system. Since this +switch will activate a builtin configuration, it may not be used together +with the +-C option. The following target +systems are currently supported:

+

+

    +
  • none
  • +
  • apple2
  • +
  • apple2enh
  • +
  • atari
  • +
  • atmos
  • +
  • c16 (works also for the c116 with memory up to 32K)
  • +
  • c64
  • +
  • c128
  • +
  • plus4
  • +
  • cbm510 (CBM-II series with 40 column video)
  • +
  • cbm610 (all CBM series-II computers with 80 column video)
  • +
  • pet (all CBM PET systems except the 2001)
  • +
  • geos
  • +
  • lunix
  • +
  • atmos
  • +
  • nes
  • +
  • supervision
  • +
+

+

There are a few more targets defined but neither of them is actually +supported.

+ + +
-u sym[:addrsize], --force-import sym[:addrsize]
+

Force an import of a symbol. While object files are always linked to the +output file, regardless if there are any references, object modules from +libraries get only linked in if an import can be satisfied by this module. +The --fore-import option may be used to add a reference to a symbol and +as a result force linkage of the module that exports the identifier.

+

The name of the symbol may optionally be followed by a colon and an address +size specifier. If no address size is specified, the default address size +for the target machine is used.

+

Please note that the symbol name needs to have the internal representation, +meaning you have to prepend an underline for C identifiers.

+ +

+

+
-v, --verbose
+

Using the -v option, you may enable more output that may help you to +locate problems. If an undefined symbol is encountered, -v causes the +linker to print a detailed list of the references (that is, source file +and line) for this symbol.

+ + +
-vm
+

Must be used in conjunction with +-m +(generate map file). Normally the map file will not include empty segments +and sections, or unreferenced symbols. Using this option, you can force the +linker to include all this information into the map file.

+ +

+

+
-C
+

This gives the name of an output config file to use. See section 4 for more +information about config files. -C may not be used together with +-t.

+ +

+

+
-D sym=value, --define sym=value
+

This option allows to define an external symbol on the command line. Value +may start with a '$' sign or with 0x for hexadecimal values, +otherwise a leading zero denotes octal values. See also the +SYMBOLS section in the configuration file.

+ +

+

+
-L path, --lib-path path
+

Specify a library search path. This option may be used more than once. It +adds a directory to the search path for library files. Libraries specified +without a path are searched in current directory, in the directory given in +the LD65_LIB environment variable, and in the list of directories +specified using --lib-path.

+ + +
-Ln
+

This option allows you to create a file that contains all global labels and +may be loaded into VICE emulator using the ll (load label) command. You +may use this to debug your code with VICE. Note: Older versions had some +bugs in the label code. If you have problems, please get the latest VICE +version.

+ +

+

+
-S addr, --start-addr addr
+

Using -S you may define the default starting address. If and how this +address is used depends on the config file in use. For the builtin +configurations, only the "none", "apple2" and "apple2enh" systems honor an +explicit start address, all other builtin config provide their own.

+ + +
-V, --version
+

This option print the version number of the linker. If you send any +suggestions or bugfixes, please include this number.

+ +

+

+
--cfg-path path
+

Specify a config file search path. This option may be used more than once. +It adds a directory to the search path for config files. A config file given +with the +-C option that has no path in +its name is searched in the current directory, in the directory given in the +LD65_CFG environment variable, and in the list of directories specified +using --cfg-path.

+ +

+

+
--dbgfile name
+

Specify an output file for debug information. Available information will be +written to this file. Using the -g option for the compiler and assembler +will increase the amount of information available. Please note that debug +information generation is currently being developed, so the format of the +file and it's contents are subject to change without further notice.

+ + +
--lib file
+

Links a library to the output. Use this command line option instead of just +naming the library file, if the linker is not able to determine the file +type because of an unusual extension.

+ + +
--obj file
+

Links an object file to the output. Use this command line option instead +of just naming the object file, if the linker is not able to determine the +file type because of an unusual extension.

+ +

+

+
--obj-path path
+

Specify an object file search path. This option may be used more than once. +It adds a directory to the search path for object files. An object file +passed to the linker that has no path in its name is searched in current +directory, in the directory given in the LD65_OBJ environment variable, +and in the list of directories specified using --obj-path.

+ +
+

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ld65-3.html b/ca65-primer/doc/ld65-3.html new file mode 100755 index 0000000..e361e77 --- /dev/null +++ b/ca65-primer/doc/ld65-3.html @@ -0,0 +1,86 @@ + + + + + ld65 Users Guide: Search paths + + + + + +Next +Previous +Contents +
+

3. Search paths

+ + +

Starting with version 2.10 there are now several search paths for files needed +by the linker: One for libraries, one for object files and one for config +files.

+ + +

3.1 Library search path +

+ + +

The library search path contains in this order:

+

+

    +
  1. The current directory.
  2. +
  3. A compiled in library path which is often /usr/lib/cc65/lib on +Linux systems.
  4. +
  5. The value of the environment variable LD65_LIB if it is defined.
  6. +
  7. A subdirectory named lib of the directory defined in the environment +variable CC65_HOME, if it is defined.
  8. +
  9. Any directory added with the +--lib-path option on the command line.
  10. +
+

+ + +

3.2 Object file search path +

+ + +

The object file search path contains in this order:

+

+

    +
  1. The current directory.
  2. +
  3. A compiled in directory which is often /usr/lib/cc65/obj on +Linux systems.
  4. +
  5. The value of the environment variable LD65_OBJ if it is defined.
  6. +
  7. A subdirectory named obj of the directory defined in the environment +variable CC65_HOME, if it is defined.
  8. +
  9. Any directory added with the +--obj-path option on the command line.
  10. +
+

+ + +

3.3 Config file search path +

+ + +

The config file search path contains in this order:

+

+

    +
  1. The current directory.
  2. +
  3. A compiled in directory which is often /usr/lib/cc65/cfg on +Linux systems.
  4. +
  5. The value of the environment variable LD65_CFG if it is defined.
  6. +
  7. A subdirectory named cfg of the directory defined in the environment +variable CC65_HOME, if it is defined.
  8. +
  9. Any directory added with the +--cfg-path option on the command line.
  10. +
+

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ld65-4.html b/ca65-primer/doc/ld65-4.html new file mode 100755 index 0000000..8488efc --- /dev/null +++ b/ca65-primer/doc/ld65-4.html @@ -0,0 +1,74 @@ + + + + + ld65 Users Guide: Detailed workings + + + + + +Next +Previous +Contents +
+

4. Detailed workings

+ + +

The linker does several things when combining object modules:

+

First, the command line is parsed from left to right. For each object file +encountered (object files are recognized by a magic word in the header, so +the linker does not care about the name), imported and exported +identifiers are read from the file and inserted in a table. If a library +name is given (libraries are also recognized by a magic word, there are no +special naming conventions), all modules in the library are checked if an +export from this module would satisfy an import from other modules. All +modules where this is the case are marked. If duplicate identifiers are +found, the linker issues a warning.

+

This procedure (parsing and reading from left to right) does mean, that a +library may only satisfy references for object modules (given directly or from +a library) named before that library. With the command line

+

+

+
+        ld65 crt0.o clib.lib test.o
+
+
+

+

the module test.o may not contain references to modules in the library +clib.lib. If this is the case, you have to change the order of the modules +on the command line:

+

+

+
+        ld65 crt0.o test.o clib.lib
+
+
+

+

Step two is, to read the configuration file, and assign start addresses +for the segments and define any linker symbols (see +Configuration files).

+

After that, the linker is ready to produce an output file. Before doing that, +it checks it's data for consistency. That is, it checks for unresolved +externals (if the output format is not relocatable) and for symbol type +mismatches (for example a zero page symbol is imported by a module as absolute +symbol).

+

Step four is, to write the actual target files. In this step, the linker will +resolve any expressions contained in the segment data. Circular references are +also detected in this step (a symbol may have a circular reference that goes +unnoticed if the symbol is not used).

+

Step five is to output a map file with a detailed list of all modules, +segments and symbols encountered.

+

And, last step, if you give the +-v switch +twice, you get a dump of the segment data. However, this may be quite +unreadable if you're not a developer:-)

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ld65-5.html b/ca65-primer/doc/ld65-5.html new file mode 100755 index 0000000..947937c --- /dev/null +++ b/ca65-primer/doc/ld65-5.html @@ -0,0 +1,617 @@ + + + + + ld65 Users Guide: Configuration files + + + + + +Next +Previous +Contents +
+

5. Configuration files

+ + +

Configuration files are used to describe the layout of the output file(s). Two +major topics are covered in a config file: The memory layout of the target +architecture, and the assignment of segments to memory areas. In addition, +several other attributes may be specified.

+

Case is ignored for keywords, that is, section or attribute names, but it is +not ignored for names and strings.

+ + + +

5.1 Memory areas +

+ + +

Memory areas are specified in a MEMORY section. Lets have a look at an +example (this one describes the usable memory layout of the C64):

+

+

+
+        MEMORY {
+            RAM1:  start = $0800, size = $9800;
+            ROM1:  start = $A000, size = $2000;
+            RAM2:  start = $C000, size = $1000;
+            ROM2:  start = $E000, size = $2000;
+        }
+
+
+

+

As you can see, there are two ram areas and two rom areas. The names +(before the colon) are arbitrary names that must start with a letter, with +the remaining characters being letters or digits. The names of the memory +areas are used when assigning segments. As mentioned above, case is +significant for these names.

+

The syntax above is used in all sections of the config file. The name +(ROM1 etc.) is said to be an identifier, the remaining tokens up to the +semicolon specify attributes for this identifier. You may use the equal sign +to assign values to attributes, and you may use a comma to separate +attributes, you may also leave both out. But you must use a semicolon to +mark the end of the attributes for one identifier. The section above may also +have looked like this:

+

+

+
+        # Start of memory section
+        MEMORY
+        {
+            RAM1:
+                start $0800
+                size $9800;
+            ROM1:
+                start $A000
+                size $2000;
+            RAM2:
+                start $C000
+                size $1000;
+            ROM2:
+                start $E000
+                size $2000;
+        }
+
+
+

+

There are of course more attributes for a memory section than just start and +size. Start and size are mandatory attributes, that means, each memory area +defined must have these attributes given (the linker will check that). I +will cover other attributes later. As you may have noticed, I've used a +comment in the example above. Comments start with a hash mark (`#'), the +remainder of the line is ignored if this character is found.

+ + +

5.2 Segments +

+ + +

Let's assume you have written a program for your trusty old C64, and you would +like to run it. For testing purposes, it should run in the RAM area. So +we will start to assign segments to memory sections in the SEGMENTS +section:

+

+

+
+        SEGMENTS {
+            CODE:   load = RAM1, type = ro;
+            RODATA: load = RAM1, type = ro;
+            DATA:   load = RAM1, type = rw;
+            BSS:    load = RAM1, type = bss, define = yes;
+        }
+
+
+

+

What we are doing here is telling the linker, that all segments go into the +RAM1 memory area in the order specified in the SEGMENTS section. So +the linker will first write the CODE segment, then the RODATA +segment, then the DATA segment - but it will not write the BSS +segment. Why? Enter the segment type: For each segment specified, you may also +specify a segment attribute. There are four possible segment attributes:

+

+

+
+        ro      means readonly
+        rw      means read/write
+        bss     means that this is an uninitialized segment
+        zp      a zeropage segment
+
+
+

+

So, because we specified that the segment with the name BSS is of type bss, +the linker knows that this is uninitialized data, and will not write it to an +output file. This is an important point: For the assembler, the BSS +segment has no special meaning. You specify, which segments have the bss +attribute when linking. This approach is much more flexible than having one +fixed bss segment, and is a result of the design decision to supporting an +arbitrary segment count.

+

If you specify "type = bss" for a segment, the linker will make sure that +this segment does only contain uninitialized data (that is, zeroes), and issue +a warning if this is not the case.

+

For a bss type segment to be useful, it must be cleared somehow by your +program (this happens usually in the startup code - for example the startup +code for cc65 generated programs takes care about that). But how does your +code know, where the segment starts, and how big it is? The linker is able to +give that information, but you must request it. This is, what we're doing with +the "define = yes" attribute in the BSS definitions. For each +segment, where this attribute is true, the linker will export three symbols.

+

+

+
+        __NAME_LOAD__   This is set to the address where the
+                        segment is loaded.
+        __NAME_RUN__    This is set to the run address of the
+                        segment. We will cover run addresses
+                        later.
+        __NAME_SIZE__   This is set to the segment size.
+
+
+

+

Replace NAME by the name of the segment, in the example above, this would +be BSS. These symbols may be accessed by your code.

+

Now, as we've configured the linker to write the first three segments and +create symbols for the last one, there's only one question left: Where does +the linker put the data? It would be very convenient to have the data in a +file, wouldn't it?

+ +

5.3 Output files +

+ + +

We don't have any files specified above, and indeed, this is not needed in a +simple configuration like the one above. There is an additional attribute +"file" that may be specified for a memory area, that gives a file name to +write the area data into. If there is no file name given, the linker will +assign the default file name. This is "a.out" or the one given with the + +-o option on the command line. Since the +default behaviour is ok for our purposes, I did not use the attribute in the +example above. Let's have a look at it now.

+

The "file" attribute (the keyword may also be written as "FILE" if you like +that better) takes a string enclosed in double quotes (`"') that specifies the +file, where the data is written. You may specify the same file several times, +in that case the data for all memory areas having this file name is written +into this file, in the order of the memory areas defined in the MEMORY +section. Let's specify some file names in the MEMORY section used above:

+

+

+
+        MEMORY {
+            RAM1:  start = $0800, size = $9800, file = %O;
+            ROM1:  start = $A000, size = $2000, file = "rom1.bin";
+            RAM2:  start = $C000, size = $1000, file = %O;
+            ROM2:  start = $E000, size = $2000, file = "rom2.bin";
+        }
+
+
+

+

The %O used here is a way to specify the default behaviour explicitly: +%O is replaced by a string (including the quotes) that contains the +default output name, that is, "a.out" or the name specified with the +-o option on the command line. Into this file, the +linker will first write any segments that go into RAM1, and will append +then the segments for RAM2, because the memory areas are given in this +order. So, for the RAM areas, nothing has really changed.

+

We've not used the ROM areas, but we will do that below, so we give the file +names here. Segments that go into ROM1 will be written to a file named +"rom1.bin", and segments that go into ROM2 will be written to a file +named "rom2.bin". The name given on the command line is ignored in both cases.

+

Assigning an empty file name for a memory area will discard the data written +to it. This is useful, if the a memory area has segments assigned that are +empty (for example because they are of type bss). In that case, the linker +will create an empty output file. This may be suppressed by assigning an empty +file name to that memory area.

+ + +

5.4 LOAD and RUN addresses (ROMable code) +

+ + +

Let us look now at a more complex example. Say, you've successfully tested +your new "Super Operating System" (SOS for short) for the C64, and you +will now go and replace the ROMs by your own code. When doing that, you +face a new problem: If the code runs in RAM, we need not to care about +read/write data. But now, if the code is in ROM, we must care about it. +Remember the default segments (you may of course specify your own):

+

+

+
+        CODE            read only code
+        RODATA          read only data
+        DATA            read/write data
+        BSS             uninitialized data, read/write
+
+
+

+

Since BSS is not initialized, we must not care about it now, but what +about DATA? DATA contains initialized data, that is, data that was +explicitly assigned a value. And your program will rely on these values on +startup. Since there's no other way to remember the contents of the data +segment, than storing it into one of the ROMs, we have to put it there. But +unfortunately, ROM is not writable, so we have to copy it into RAM before +running the actual code.

+

The linker cannot help you copying the data from ROM into RAM (this must be +done by the startup code of your program), but it has some features that will +help you in this process.

+

First, you may not only specify a "load" attribute for a segment, but +also a "run" attribute. The "load" attribute is mandatory, and, if +you don't specify a "run" attribute, the linker assumes that load area +and run area are the same. We will use this feature for our data area:

+

+

+
+        SEGMENTS {
+            CODE:   load = ROM1, type = ro;
+            RODATA: load = ROM2, type = ro;
+            DATA:   load = ROM2, run = RAM2, type = rw, define = yes;
+            BSS:    load = RAM2, type = bss, define = yes;
+        }
+
+
+

+

Let's have a closer look at this SEGMENTS section. We specify that the +CODE segment goes into ROM1 (the one at $A000). The readonly data +goes into ROM2. Read/write data will be loaded into ROM2 but is run +in RAM2. That means that all references to labels in the DATA +segment are relocated to be in RAM2, but the segment is written to +ROM2. All your startup code has to do is, to copy the data from it's +location in ROM2 to the final location in RAM2.

+

So, how do you know, where the data is located? This is the second point, +where you get help from the linker. Remember the "define" attribute? +Since we have set this attribute to true, the linker will define three +external symbols for the data segment that may be accessed from your code:

+

+

+
+        __DATA_LOAD__   This is set to the address where the segment
+                        is loaded, in this case, it is an address in
+                        ROM2.
+        __DATA_RUN__    This is set to the run address of the segment,
+                        in this case, it is an address in RAM2.
+        __DATA_SIZE__   This is set to the segment size.
+
+
+

+

So, what your startup code must do, is to copy __DATA_SIZE__ bytes from +__DATA_LOAD__ to __DATA_RUN__ before any other routines are called. +All references to labels in the DATA segment are relocated to RAM2 +by the linker, so things will work properly.

+ + +

5.5 Other MEMORY area attributes +

+ + +

There are some other attributes not covered above. Before starting the +reference section, I will discuss the remaining things here.

+

You may request symbols definitions also for memory areas. This may be +useful for things like a software stack, or an i/o area.

+

+

+
+        MEMORY {
+            STACK:  start = $C000, size = $1000, define = yes;
+        }
+
+
+

+

This will define three external symbols that may be used in your code:

+

+

+
+        __STACK_START__         This is set to the start of the memory
+                                area, $C000 in this example.
+        __STACK_SIZE__          The size of the area, here $1000.
+        __STACK_LAST__          This is NOT the same as START+SIZE.
+                                Instead, it it defined as the first
+                                address that is not used by data. If we
+                                don't define any segments for this area,
+                                the value will be the same as START.
+
+
+

+

A memory section may also have a type. Valid types are

+

+

+
+        ro      for readonly memory
+        rw      for read/write memory.
+
+
+

+

The linker will assure, that no segment marked as read/write or bss is put +into a memory area that is marked as readonly.

+

Unused memory in a memory area may be filled. Use the "fill = yes" +attribute to request this. The default value to fill unused space is zero. If +you don't like this, you may specify a byte value that is used to fill these +areas with the "fillval" attribute. This value is also used to fill unfilled +areas generated by the assemblers .ALIGN and .RES directives.

+

The symbol %S may be used to access the default start address (that is, +the one defined in the +FEATURES section, or the +value given on the command line with the +-S +option).

+ + +

5.6 Other SEGMENT attributes +

+ + +

Segments may be aligned to some memory boundary. Specify "align = num" to +request this feature. Num must be a power of two. To align all segments on a +page boundary, use

+

+

+
+        SEGMENTS {
+            CODE:   load = ROM1, type = ro, align = $100;
+            RODATA: load = ROM2, type = ro, align = $100;
+            DATA:   load = ROM2, run = RAM2, type = rw, define = yes,
+                    align = $100;
+            BSS:    load = RAM2, type = bss, define = yes, align = $100;
+        }
+
+
+

+

If an alignment is requested, the linker will add enough space to the output +file, so that the new segment starts at an address that is dividable by the +given number without a remainder. All addresses are adjusted accordingly. To +fill the unused space, bytes of zero are used, or, if the memory area has a +"fillval" attribute, that value. Alignment is always needed, if you have +used the .ALIGN command in the assembler. The alignment of a segment +must be equal or greater than the alignment used in the .ALIGN command. +The linker will check that, and issue a warning, if the alignment of a segment +is lower than the alignment requested in an .ALIGN command of one of the +modules making up this segment.

+

For a given segment you may also specify a fixed offset into a memory area or +a fixed start address. Use this if you want the code to run at a specific +address (a prominent case is the interrupt vector table which must go at +address $FFFA). Only one of ALIGN or OFFSET or START may be +specified. If the directive creates empty space, it will be filled with zero, +of with the value specified with the "fillval" attribute if one is given. +The linker will warn you if it is not possible to put the code at the +specified offset (this may happen if other segments in this area are too +large). Here's an example:

+

+

+
+        SEGMENTS {
+            VECTORS: load = ROM2, type = ro, start = $FFFA;
+        }
+
+
+

+

or (for the segment definitions from above)

+

+

+
+        SEGMENTS {
+            VECTORS: load = ROM2, type = ro, offset = $1FFA;
+        }
+
+
+

+

The "align", "start" and "offset" attributes change placement +of the segment in the run memory area, because this is what is usually +desired. If load and run memory areas are equal (which is the case if only the +load memory area has been specified), the attributes will also work. There is +also an "align_load" attribute that may be used to align the start of the +segment in the load memory area, in case different load and run areas have +been specified. There are no special attributes to set start or offset for +just the load memory area.

+

To suppress the warning, the linker issues if it encounters a segment that is +not found in any of the input files, use "optional=yes" as additional +segment attribute. Be careful when using this attribute, because a missing +segment may be a sign of a problem, and if you're suppressing the warning, +there is no one left to tell you about it.

+ +

5.7 The FILES section +

+ + +

The FILES section is used to support other formats than straight binary +(which is the default, so binary output files do not need an explicit entry +in the FILES section).

+

The FILES section lists output files and as only attribute the format of +each output file. Assigning binary format to the default output file would +look like this:

+

+

+
+        FILES {
+            %O: format = bin;
+        }
+
+
+

+

The only other available output format is the o65 format specified by Andre +Fachat (see the +6502 binary relocation format specification). It is defined like this:

+

+

+
+        FILES {
+            %O: format = o65;
+        }
+
+
+

+

The necessary o65 attributes are defined in a special section labeled +FORMAT.

+ + + +

5.8 The FORMAT section +

+ + +

The FORMAT section is used to describe file formats. The default (binary) +format has currently no attributes, so, while it may be listed in this +section, the attribute list is empty. The second supported format, o65, has +several attributes that may be defined here.

+

+

+
+    FORMATS {
+        o65: os = lunix, version = 0, type = small,
+             import = LUNIXKERNEL,
+             export = _main;
+    }
+
+
+

+ + + +

5.9 The FEATURES section +

+ + +

In addition to the MEMORY and SEGMENTS sections described above, the +linker has features that may be enabled by an additional section labeled +FEATURES.

+ + +

The CONDES feature

+ + +

CONDES is used to tell the linker to emit module constructor/destructor +tables.

+

+

+
+        FEATURES {
+            CONDES: segment = RODATA,
+                    type = constructor,
+                    label = __CONSTRUCTOR_TABLE__,
+                    count = __CONSTRUCTOR_COUNT__;
+        }
+
+
+

+

The CONDES feature has several attributes:

+

+

+ +
segment
+

This attribute tells the linker into which segment the table should be +placed. If the segment does not exist, it is created.

+ + +
type
+

Describes the type of the routines to place in the table. Type may be one of +the predefined types constructor, destructor, interruptor, or +a numeric value between 0 and 6.

+ + +
label
+

This specifies the label to use for the table. The label points to the start +of the table in memory and may be used from within user written code.

+ + +
count
+

This is an optional attribute. If specified, an additional symbol is defined +by the linker using the given name. The value of this symbol is the number +of entries (not bytes) in the table. While this attribute is optional, +it is often useful to define it.

+ + +
order
+

Optional attribute that takes one of the keywords increasing or +decreasing as an argument. Specifies the sorting order of the entries +within the table. The default is increasing, which means that the +entries are sorted with increasing priority (the first entry has the lowest +priority). "Priority" is the priority specified when declaring a symbol as +.CONDES with the assembler, higher values mean higher priority. You may +change this behaviour by specifying decreasing as the argument, the +order of entries is reversed in this case.

+

Please note that the order of entries with equal priority is undefined.

+ +
+

+

Without specifying the CONDES feature, the linker will not create any +tables, even if there are condes entries in the object files.

+

For more information see the .CONDES command in the +ca65 manual.

+ + +

The STARTADDRESS feature

+ + +

STARTADDRESS is used to set the default value for the start address, +which can be referenced by the %S symbol. The builtin default for the +linker is $200.

+

+

+
+        FEATURES {
+            # Default start address is $1000
+            STARTADDRESS:       default = $1000;
+        }
+
+
+

+

Please note that order is important: The default start address must be defined +before the %S symbol is used in the config file. This does usually +mean, that the FEATURES section has to go to the top of the config file.

+ + + +

5.10 The SYMBOLS section +

+ + +

The configuration file may also be used to define symbols used in the link +stage. The mandatory attribute for a symbol is its value. A second, boolean +attribute named weak is available. If a symbol is marked as weak, it may +be overridden by defining a symbol of the same name from the command line. The +default for symbols is that they're strong, which means that an attempt to +define a symbol with the same name from the command line will lead to an +error.

+

The following example defines the stack size for an application, but allows +the programmer to override the value by specifying --define +__STACKSIZE__=xxx on the command line.

+

+

+
+        SYMBOLS {
+            # Define the stack size for the application
+            __STACKSIZE__:      value = $800, weak = yes;
+        }
+
+
+

+ + + +

5.11 Builtin configurations +

+ + +

The builtin configurations are part of the linker source. They can be retrieved +with --dump-config and don't have a special format. So if you need a +special configuration, it's a good idea to start with the builtin configuration +for your system. In a first step, just replace -t target by -C +configfile. Then go on and modify the config file to suit your needs.

+ + + +

5.12 Secondary configurations +

+ + +

Several machine specific binary packages are distributed together with secondary +configurations (in the cfg directory). These configurations can be used with +-C configfile too.

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ld65-6.html b/ca65-primer/doc/ld65-6.html new file mode 100755 index 0000000..487edc9 --- /dev/null +++ b/ca65-primer/doc/ld65-6.html @@ -0,0 +1,61 @@ + + + + + ld65 Users Guide: Special segments + + + + + +Next +Previous +Contents +
+

6. Special segments

+ + +

The builtin config files do contain segments that have a special meaning for +the compiler and the libraries that come with it. If you replace the builtin +config files, you will need the following information.

+ +

6.1 INIT +

+ + +

The INIT segment is used for initialization code that may be reused once +execution reaches main() - provided that the program runs in RAM. You +may for example add the INIT segment to the heap in really memory +constrained systems.

+ +

6.2 LOWCODE +

+ + +

For the LOWCODE segment, it is guaranteed that it won't be banked out, so it +is reachable at any time by interrupt handlers or similar.

+ +

6.3 STARTUP +

+ + +

This segment contains the startup code which initializes the C software stack +and the libraries. It is placed in its own segment because it needs to be +loaded at the lowest possible program address on several platforms.

+ +

6.4 ZPSAVE +

+ + +

The ZPSAVE segment contains the original values of the zeropage locations used +by the ZEROPAGE segment. It is placed in its own segment because it must not be +initialized.

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ld65-7.html b/ca65-primer/doc/ld65-7.html new file mode 100755 index 0000000..5d1d5c6 --- /dev/null +++ b/ca65-primer/doc/ld65-7.html @@ -0,0 +1,30 @@ + + + + + ld65 Users Guide: Bugs/Feedback + + + + + +Next +Previous +Contents +
+

7. Bugs/Feedback

+ + +

If you have problems using the linker, if you find any bugs, or if you're +doing something interesting with it, I would be glad to hear from you. Feel +free to contact me by email ( +uz@cc65.org).

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ld65-8.html b/ca65-primer/doc/ld65-8.html new file mode 100755 index 0000000..a1e145c --- /dev/null +++ b/ca65-primer/doc/ld65-8.html @@ -0,0 +1,46 @@ + + + + + ld65 Users Guide: Copyright + + + + +Next +Previous +Contents +
+

8. Copyright

+ + +

ld65 (and all cc65 binutils) are (C) Copyright 1998-2005 Ullrich von +Bassewitz. For usage of the binaries and/or sources the following +conditions do apply:

+

This software is provided 'as-is', without any expressed or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software.

+

Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions:

+

+

    +
  1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required.
  2. +
  3. Altered source versions must be plainly marked as such, and must not +be misrepresented as being the original software.
  4. +
  5. This notice may not be removed or altered from any source +distribution.
  6. +
+

+ + + +
+Next +Previous +Contents + + diff --git a/ca65-primer/doc/ld65.html b/ca65-primer/doc/ld65.html new file mode 100755 index 0000000..ca8bf8c --- /dev/null +++ b/ca65-primer/doc/ld65.html @@ -0,0 +1,81 @@ + + + + + ld65 Users Guide + + + + + +Next +Previous +Contents +
+

ld65 Users Guide

+ +

Ullrich von Bassewitz, +uz@cc65.org

02.12.2000, 02.10.2001 +
+The ld65 linker combines object files into an executable file. ld65 is highly +configurable and uses configuration files for high flexibility. +
+

+

1. Overview

+ +

+

2. Usage

+ + +

+

3. Search paths

+ + +

+

4. Detailed workings

+ +

+

5. Configuration files

+ + +

+

6. Special segments

+ + +

+

7. Bugs/Feedback

+ +

+

8. Copyright

+ +
+Next +Previous +Contents + + diff --git a/ca65-primer/gamecart/GAME.bat b/ca65-primer/gamecart/GAME.bat new file mode 100755 index 0000000..bb64620 --- /dev/null +++ b/ca65-primer/gamecart/GAME.bat @@ -0,0 +1,5 @@ +@echo on +ca65.exe --cpu 6502 --listing --include-dir . GAME.s ld65.exe -C GAME.cfg -m GAME.map -o GAME.a0 GAME.o VIC-SSS-MMX.o +pause +REM xvic -ntsc -sound -memory none -cartA GAME.a0 +exit diff --git a/ca65-primer/gamecart/GAME.cfg b/ca65-primer/gamecart/GAME.cfg new file mode 100755 index 0000000..7eec1f7 --- /dev/null +++ b/ca65-primer/gamecart/GAME.cfg @@ -0,0 +1,20 @@ +# VIC 20 8K ROM autostart game cartridge +MEMORY { + ZP: start = $0000, size = $0100, type = rw; + RAM0: start = $0400, size = $0C00, type = rw; + RAM: start = $1000, size = $1000, type = rw; + RAM1: start = $2000, size = $2000, type = rw; + RAM2: start = $4000, size = $2000, type = rw; + RAM3: start = $6000, size = $2000, type = rw; + ROM: start = $A000, size = $2000, type = ro, fill = yes; +} +SEGMENTS { + BOOT: load = ROM, type = ro, define = yes, optional = no; + STARTUP: load = ROM, type = ro, define = yes, optional = no; + CODE: load = ROM, type = ro, define = yes, optional = no; + SPRITE: load = ROM, type = ro, define = yes, optional = yes; + RODATA: load = ROM, type = ro, define = yes, optional = yes; +} +FEATURES { + STARTADDRESS: default = $A000; +} diff --git a/ca65-primer/gamecart/GAME.s b/ca65-primer/gamecart/GAME.s new file mode 100755 index 0000000..c22211a --- /dev/null +++ b/ca65-primer/gamecart/GAME.s @@ -0,0 +1,139 @@ +;********************************************************************* +; YOUR PROJECT INFORMATION GOES HERE +; and then save file as: yourgame.s +; + .fileopt author, "your name" + .fileopt comment, "your comments" + .fileopt compiler, "VIC 20 ASSEMBLER" + +;********************************************************************* +; VIC 20 ROM autostart game cartridge +; written by Robert Hurst +; updated version: 19-Apr-2010 +; +; ca65.exe --cpu 6502 --listing --include-dir . yourgame.s +; +; - for 8K GAME CARTRIDGE ROM games: +; ld65.exe -C GAME.cfg -o yourgame.a0 yourgame.o VIC-SSS-MMX.o +; + .include "VIC-SSS-MMX.h" + +;********************************************************************* +; Commodore ROM cartridge boot sequence +; load address of startup & restore key: +; + .segment "BOOT" + + .word MAIN + .word NMI + ; power-up signature +A0CBM: .byte $41, $30, $C3, $C2, $CD + +;********************************************************************* +; Starting entry point for this program +; + .segment "STARTUP" + +MAIN: + ; initialize VIC Kernal + JSR $FD8D ; ramtas Initialise System Constants (memory pointers) + JSR $FD52 ; restor Restore Kernal vectors (at 0314) + JSR $FDF9 ; ioinit Initialise I/O (timers are enabled) + JSR $E518 ; cint1 Initialize I/O (VIC reset, must follow ramtas) + LDA SCRNPAGE + STA ACTUAL + ; LDA #$7F + ; STA $911E ; disable NMIs (Restore key) + ; + ; initialize VIC BASIC + JSR $E45B ; initv Initialize vectors + JSR $E3A4 ; initcz Initialize BASIC RAM + JSR $E404 ; initms Output power-up message + ; + LDA MACHINE + CMP #$05 + BEQ NTSC + CMP #$0C + BEQ PAL + JMP READY ; not a VIC? + ; + ; NTSC setup +NTSC: LDX #<@NTSC ; load the timer low-byte latches + STX $9126 + LDX #>@NTSC + LDA #$75 ; raster line 234/235 + BNE IRQSYNC +@NTSC = $4243 ; (261 * 65 - 2) + ; + ; PAL setup +PAL: LDX #<@PAL ; load the timer low-byte latches + STX $9126 + LDX #>@PAL + LDA #$82 ; raster line 260/261 + BNE IRQSYNC +@PAL = $5686 ; (312 * 71 - 2) + ; +IRQSYNC: + CMP VIC+$04 + BNE IRQSYNC + STX $9125 ; load T1 latch high, and transfer both bytes to T1 counter + JMP RESTART + +;********************************************************************* +; enter VIC BASIC mode +; +READY: + LDA ACTUAL + STA SCRNPAGE ; restore video page back to VIC startup value + JSR $E518 ; cint1 Initialize I/O (VIC reset, must follow ramtas) + JSR $E45B ; initv Initialize vectors + JSR $E3A4 ; initcz Initialize BASIC RAM + LDA #$01 ; just to have some fun: + STA RVSFLAG ; - character reverse flag + LDA #$03 ; - set color to CYAN + STA COLORCODE + JSR $E404 ; initms Output power-up message + LDA #$06 ; set color back to BLUE + STA COLORCODE ; because we're used to it + JMP $E467 ; bassft BASIC Warm Start + +;********************************************************************* +; RESTORE key was pressed +NMI: + ; JMP $FEC7 ; continue + CLD + PLA ; restore Y register + TAY + PLA ; restore X register + TAX + PLA ; restore Accumulator + ; RTI ; continue + PLA + PLA + LDA #$FF ; acknowledge and clear + STA $9122 ; interrupts + LDY $9111 + JMP RESTART + + +;********************************************************************* +; Your main program code starts here +; + .segment "CODE" + +RESTART: + .global RESTART + LDA #$00 + TAX + LDY #$18 + STX $FD + STY $FE + TAY +@loop: STA ($FD),Y + INY + BNE @loop + INC $FE + LDX $FE + CPX #$1C + BNE @loop + diff --git a/ca65-primer/gamecart/GAME.sh b/ca65-primer/gamecart/GAME.sh new file mode 100755 index 0000000..1ef0df7 --- /dev/null +++ b/ca65-primer/gamecart/GAME.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# + +TITLE=demo + +set -o xtrace +ca65 --cpu 6502 --listing --include-dir . -o VIC-SSS-MMX.o ../VIC-SSS-MMX.s +ca65 --cpu 6502 --listing --include-dir . $TITLE.s +ca65 --cpu 6502 --listing --include-dir . GAME.s +ld65 -C GAME.cfg -Ln $TITLE.sym -m $TITLE.map -o $TITLE.a0 GAME.o $TITLE.o VIC-SSS-MMX.o +ca65 --cpu 6502 --listing --include-dir . -o basic.o ../basic.s +ld65 -C ../basic+8k.cfg -o ../demos/$TITLE.prg basic.o $TITLE.o VIC-SSS-MMX.o +set +o xtrace + +echo -n "Press RETURN: " && read N + +# UNCOMMENT YOUR CHOICE OF EMULATORS ... + +#mess -debug vic20 -cart $TITLE.a0 +#xvic -cartA $TITLE.a0 + +exit + diff --git a/ca65-primer/gamecart/SSS.h b/ca65-primer/gamecart/SSS.h new file mode 100755 index 0000000..21ff867 --- /dev/null +++ b/ca65-primer/gamecart/SSS.h @@ -0,0 +1,67 @@ +;********************************************************************* +; Commodore VIC 20 Software Sprite Stack - MMX Edition +; written by Robert Hurst +; last updated: 30-Oct-2011 +; +; use this for hard-coding RAM memory locations for +; sprite registers and run-time variables ... +; +SSSBUF = $1800 ; this can be resized smaller as required -- + ; if all 64-chars are used by sprites, that + ; exhausts all 128 custom characters for + ; double-buffering (x2) +; +; SPRITE REGISTERS +; +SPRITEBACK = SSSBUF + 64*8 ; 1st char this sprite is in collision with +SPRITEBUFH = SPRITEBACK + SPRITEMAX ; pointer within sprite image buffer +SPRITEBUFL = SPRITEBUFH + SPRITEMAX ; pointer within sprite image buffer +SPRITEC1H = SPRITEBUFL + SPRITEMAX ; pointer within sprite display character pool +SPRITEC1L = SPRITEC1H + SPRITEMAX ; pointer within sprite display character pool +SPRITEC2H = SPRITEC1L + SPRITEMAX ; pointer within sprite display character pool +SPRITEC2L = SPRITEC2H + SPRITEMAX ; pointer within sprite display character pool +SPRITECOL = SPRITEC2L + SPRITEMAX ; 4-bit VIC color code +SPRITECX = SPRITECOL + SPRITEMAX ; sprite collision X-coord +SPRITECY = SPRITECX + SPRITEMAX ; sprite collision Y-coord +SPRITEDEF = SPRITECY + SPRITEMAX ; function/matrix definition (see explanation below) +SPRITEH = SPRITEDEF + SPRITEMAX ; number of raster lines (1-16) +SPRITEIMGH = SPRITEH + SPRITEMAX ; pointer to source graphic for rendering at 0,0 +SPRITEIMGL = SPRITEIMGH + SPRITEMAX ; pointer to source graphic for rendering at 0,0 +SPRITEX = SPRITEIMGL + SPRITEMAX ; horizontal pixel coordinate, visible >0 - 0 - +; last updated: 30-Oct-2011 +; +; === IMPORTANT === +; required symbols you need to define for your game: +; +;SPRITEDEF4 = $10 ; un-comment this for "repeating" flag usage +SPRITEDEF5 = $20 ; un-comment this for "ghost" flag usage +SPRITEDEF6 = $40 ; un-comment this for "collision" flag usage +SPRITEWIDE = 1 ; comment this out to skip 16-bit wide sprites +SPRITEMAX = 16 ; reserves this many SPRITE registers (1-?) +SSSNULL = $A0 ; your character used for a blank background + +;********************************************************************* +; some pertinent VIC 20 symbols +; +RNDSEED = $8B ; -$8F: BASIC RND seed value +JIFFYH = $A0 ; jiffy clock high +JIFFYM = $A1 ; jiffy clock med +JIFFYL = $A2 ; jiffy clock low +DATANEXT = $A6 ; DATASETTE pointer (0-191) +KEYCHARS = $C6 ; number of characters in KEYBUF (0-10) +RVSFLAG = $C7 ; character reverse flag +PLAYROWS = $C8 ; current screen row length (16-24) +CURSOR = $CC ; cursor enable (0=flash) +CRSRCHAR = $CE ; character under cursor +SCRNLINE = $D1 ; pointer to cursor's screen line +CRSRCOL = $D3 ; position of cursor on screen line +PLAYCOLS = $D5 ; current screen line length (16-24) +CRSRROW = $D6 ; screen row where cursor is +COLORLINE = $F3 ; pointer to cursor's color line +INPUT = $0200 ; -$0258: 89-character BASIC INPUT buffer +KEYBUF = $0277 ; -$0280: 10-character keyboard buffer +COLORCODE = $0286 ; current cursor color +CRSRCOLOR = $0287 ; color under cursor +SCRNPAGE = $0288 ; active screen memory page (unexpanded = $1E) +SHIFTMODE = $0291 ; 0=allow, 128=locked +SCROLLFLAG = $0292 ; auto scrolldown flag +ACOPY = $030C ; temp storage for A register +XCOPY = $030D ; temp storage for X register +YCOPY = $030E ; temp storage for Y register +DATASETTE = $033C ; -$03FB: 192-byte tape input buffer +MASK = $8270 ; ROM character $40 - Shift-M (\) +VIC = $9000 ; start of Video Interface Chip registers +MACHINE = $EDE4 ; NTSC=$05, PAL=$0C +STOPKEY = $F770 ; check for STOP key pressed +RESET = $FD22 ; warm startup +CHROUT = $FFD2 ; print character with cursor translation +GETIN = $FFE4 ; get a character from keyboard queue + +;********************************************************************* +; volatile VIC-SSS symbols +; +VECTORBG = $01 ; sprite temp pointer to an image source +DIRTYLINE2 = $59 ; -$70: 24 screen rows for last dirty column +1 +NEWDIRT = $BF ; bit 7=VIDEO1, 6=VIDEO2, 5=PLAYFIELD, 4=STATIC +DIRTYLINE = $D9 ; -$F0: 24 screen rows for starting dirty column +DIRTMAP = $F1 ; pointer to PLAYCOLOR for dirty-bit updates +VECTORFG = $F7 ; sprite temp pointer to an image target +VECTOR1 = $F9 ; sprite temp pointer +VECTOR2 = $FB ; sprite temp pointer +VECTOR3 = $FD ; sprite temp pointer +FPS = $0285 ; number of VIC re-directions every 64-jiffies +PENDING = $0293 ; next video page: $10 or $12 +ACTUAL = $0294 ; save VIC startup video page +VSYNC = $0295 ; set when waiting for vertical sync(s) +VSYNC2 = $0296 ; frames skipped +VCOUNT = $0297 ; current SSSFLIP count +SSSCLIPX = $0298 ; pixels to right border: 8 * (PLAYCOLS + 2) +SSSCLIPY = $0299 ; pixels to bottom border: 8 * (PLAYROWS + 2) +R0 = $029A ; unused temporary register +R1 = $029B ; unused temporary register +R2 = $029C ; unused temporary register +R3 = $029D ; unused temporary register +R4 = $029E ; unused temporary register + +;********************************************************************* +; FRAME REGISTERS +; +VICFRAME1 = $1000 ; first video buffer +VICCOLOR1 = $9400 ; first color buffer +VICFRAME2 = $1200 ; second video buffer +VICCOLOR2 = $9600 ; second color buffer +PLAYFIELD = $1400 ; write-pending screen buffer +PLAYCOLOR = $1600 ; write-pending color buffer (bits 0-3) + ; bit 4 = static cell bit, sprites go behind + ; bit 5 = dirty bit for pending page + ; bit 6 = dirty bit for video page 2 only + ; bit 7 = dirty bit for video page 1 only + +;********************************************************************* +; SPRITE REGISTERS +; +.global SSSBUF ; defaults to $1800, but can be relocated by linker +.global SPRITEBACK ; character code this sprite is in collision with +.global SPRITEBUFH ; pointer within sprite image buffer @ $1800 - $19FF +.global SPRITEBUFL +.global SPRITEC1H ; pointer within sprite display character pool +.global SPRITEC1L +.global SPRITEC2H ; pointer within sprite display character pool +.global SPRITEC2L +.global SPRITECOL ; 4-bit VIC color code +.global SPRITECX ; sprite collision X-coord +.global SPRITECY ; sprite collision Y-coord +.global SPRITEDEF ; matrix definition: + ; bit 0: height 0 = 8px; 1 = 16px + ; bit 1: width 0 = 8px; 1 = 16px + ; bit 2: float Y 0=fixed cell; 1=vertical float + ; bit 3: float X 0=fixed cell; 1=horizontal float + ; bit 4: repeat 0=independent; 1=re-use previous + ; bit 5: ghost 0=merge image; 1=invert image + ; bit 6: collision 0=ignore; 1=detect + ; bit 7: enabled 0=invisible; 1=visible +.global SPRITEH ; number of raster lines (1-16) +.global SPRITEIMGH ; pointer to source graphic for rendering at 0,0 +.global SPRITEIMGL +.global SPRITEX ; horizontal pixel coordinate, visible >0 - 0 - ', $BE +.charmap '?', $BF +.charmap '~', $DE ; PI symbol + diff --git a/ca65-primer/gamecart/demo.a0 b/ca65-primer/gamecart/demo.a0 new file mode 100755 index 0000000..04217f9 Binary files /dev/null and b/ca65-primer/gamecart/demo.a0 differ diff --git a/ca65-primer/gamecart/demo.s b/ca65-primer/gamecart/demo.s new file mode 100755 index 0000000..987b10a --- /dev/null +++ b/ca65-primer/gamecart/demo.s @@ -0,0 +1,772 @@ +;********************************************************************* +; DEMO featuring VIC Software Sprite Stack - MMX edition +; written by Robert Hurst +; updated version: 19-Apr-2010 +;********************************************************************* + + .include "VIC-SSS-MMX.h" + .include "SSS.h" + + .segment "CODE" + + LDA #$1B + STA VIC+$0F + LDA #6 + STA COLORCODE + JSR SSSINIT ; must occur BEFORE re-directing IRQ +; +; implement my interrupt vector + SEI + LDX #SSSIRQ + STX $0314 + STY $0315 + CLI + + LDA #%10001101 ; enable an 8x16 sprite that floats X-Y + LDY #16 ; pixel height + JSR SSSCREATE + LDX #0 + STX R0 + LDY #(8*12)+16 + JSR SSSMOVEXY + + LDA #%10001100 ; enable an 8x8 sprite that floats X-Y + LDY #8 ; pixel height + JSR SSSCREATE + LDA SSSCLIPX + CLC + ADC #8 + TAX + LDY #(8*9)+20 + JSR SSSMOVEXY + + LDA #%10001011 ; enable an 16x16 sprite that floats X + LDY #16 ; pixel height + JSR SSSCREATE + LDX #0 + STX R1 + LDY #(8*15)+16 + JSR SSSMOVEXY + +TITLE: + JSR SSSPRINTS + .byte $F2,"SOFTWARE ",$F5,"SPRITE ",$F6,"STACK",13,13,0 + JSR SSSPRINTS + .byte $F0," A TUTORIAL DEMO",13,0 + JSR SSSPRINTS + .byte $F4," BY ROBERT HURST",13,13,0 + JSR SSSPRINTS + .byte $F3," RELEASED 19-APR-2010" + .byte 0 +@loop: JSR HEROMOVE + JSR QMANMOVE + JSR ANYKEY + BEQ @loop + +DEMO1: + JSR SSSPRINTS + .byte $F4,"THE FIRST THING TO DO",13 + .byte "IS TO INITIALIZE THE",13 + .byte "SOFTWARE SPRITE STACK:",13,13,0 + JSR SSSPRINTS + .byte $F6,"JSR ",$F2,"SSSINIT",13,13,13 + .byte $F0,"IT PUTS THE VIC IN A",13 + .byte "DUAL-BUFFER VIDEO MODE" + .byte $F6," @ $1000 & $1200",13 + .byte $F0,"A PLAYFIELD AND COLOR",13 + .byte $F6," @ $1400 & $1600",13 + .byte $F0,"REGISTERS AND STORAGE",13 + .byte $F6," @ $1800 - $1B7F",13 + .byte $F0,"USES CUSTOM CHARACTERS" + .byte $F6," @ $1C00 - $1FFF" + .byte 0 +@loop: JSR ANYKEY + BEQ @loop + +DEMO2: + JSR SSSPRINTS + .byte $F4,"WRITING TO THE",13 + .byte "PLAYFIELD:",13,13 + .byte $F6,"LDY #",$F5,"4",13 + .byte $F6,"LDX #",$F5,"18",13 + .byte $F6,"JSR ",$F2,"SSSPLOT",13 + .byte $F6,"JSR ",$F2,"SSSPRINTS",13 + .byte $F0,".ASCIIZ ",$F5,"'ABC'",13,13 + .byte $F6,"LDA #",$F5,"6",13 + .byte $F6,"STA ",$F4,"COLORCODE",13 + .byte $F6,"LDY #",$F5,"6",13 + .byte $F6,"LDX #",$F5,"19",13 + .byte $F6,"JSR ",$F2,"SSSPLOT",13 + .byte $F6,"LDA #",$F5,"$84",13 + .byte $F6,"JSR ",$F2,"SSSPOKE",13,13 + .byte $F6,"LDY #",$F5,"0",13 + .byte $F6,"JSR ",$F2,"SSSFLIP",13 + .byte 0 + LDY #4 + LDX #18 + JSR SSSPLOT + JSR SSSPRINTS + .asciiz "ABC" + LDY #6 + LDX #19 + JSR SSSPLOT + LDA #6 + STA COLORCODE + LDA #$84 + JSR SSSPOKE +@loop: JSR ANYKEY + BEQ @loop + +DEMO3: + JSR SSSPRINTS + .byte $F4,"MAKING A SIMPLE",13 + .byte "SPRITE (",$F2,"RED HEART",$F4,"):",13,13 + .byte $F6,"LDA #",$F5,"%10001100",13 + .byte $F6,"LDY #",$F5,"8",13 + .byte $F6,"JSR ",$F2,"SSSCREATE",13,13 + .byte $F6,"LDA #",$F5,"2",13 + .byte $F6,"LDX #",$F5,"$98",13 + .byte $F6,"LDY #",$F5,"$82",13 + .byte $F6,"JSR ",$F2,"SSSANIM",13,13 + .byte $F6,"LDX #",$F5,"160",13 + .byte $F6,"LDY #",$F5,"96",13 + .byte $F6,"JSR ",$F2,"SSSMOVEXY",13,13 + .byte $F6,"LDY #",$F5,"0",13 + .byte $F6,"JSR ",$F2,"SSSFLIP" + .byte 0 + JSR HEART +@loop: JSR ANYKEY + BEQ @loop + +DEMO4: + JSR SSSPRINTS + .byte $F4,"MOVING A SIMPLE",13 + .byte "SPRITE:",13,13 + .byte $F0,"@LOOP:",13 + .byte $F6,"LDX #",$F5,"0",13 + .byte $F6,"JSR ",$F2,"SSSUSE",13,13 + .byte $F6,"LDX ",$F4,"SPRITEX",13 + .byte $F6,"INC ",$F4,"SPRITEY",13 + .byte $F6,"LDY ",$F4,"SPRITEY",13 + .byte $F6,"JSR ",$F2,"SSSMOVEXY",13,13 + .byte $F6,"LDY #",$F5,"1",13 + .byte $F6,"JSR ",$F2,"SSSFLIP",13 + .byte $F6,"JMP ",$F0,"@LOOP",13 + .byte 0 + JSR HEART +@loop: LDX #0 + JSR SSSUSE + LDX SPRITEX + INC SPRITEY + LDY SPRITEY + JSR SSSMOVEXY + JSR ANYKEY + BEQ @loop + +DEMO5: + JSR SSSPRINTS + .byte $F4,"STATIC CELLS ON",13 + .byte "THE PLAYFIELD:",13,13 + .byte $F6,"LDY #",$F5,"10",13 + .byte $F6,"LDX #",$F5,"18",13 + .byte $F6,"JSR ",$F2,"SSSPLOT",13 + .byte $F6,"LDA #",$F5,"6",13 + .byte $F6,"STA ",$F4,"COLORCODE",13 + .byte $F6,"LDA #",$F5,"128+81",13 + .byte $F6,"JSR ",$F2,"SSSCELL",13 + .byte $F6,"LDY #",$F5,"0",13 + .byte $F6,"JSR ",$F2,"SSSFLIP",13 + .byte $F6,"LDY #",$F5,"10",13 + .byte $F6,"LDX #",$F5,"18",13 + .byte $F6,"JSR ",$F2,"SSSPLOT",13 + .byte $F6,"LDA #",$F5,"3",13 + .byte $F6,"STA ",$F4,"COLORCODE",13 + .byte $F6,"LDA #",$F5,"128+87",13 + .byte $F6,"JSR ",$F2,"SSSCELL",13 + .byte $F6,"LDY #",$F5,"0",13 + .byte $F6,"JSR ",$F2,"SSSFLIP",13 + .byte 0 + ; + JSR HEART + JSR BALL + ; +@loop: LDX #0 + JSR SSSUSE + LDX SPRITEX + INC SPRITEY + LDY SPRITEY + JSR SSSMOVEXY + JSR ANYKEY + BEQ @loop + +DEMO6: + JSR SSSPRINTS + .byte $F4,"COLLISION",13 + .byte "DETECTION:",13,13 + .byte $F6,"LDA ",$F4,"SPRITEDEF",13 + .byte $F6,"ORA #",$F5,"$40",13 + .byte $F6,"STA ",$F4,"SPRITEDEF",13 + .byte $F7,"... MOVE SPRITE",13 + .byte $F6,"LDY #",$F5,"0",13 + .byte $F6,"JSR ",$F2,"SSSFLIP",13,13 + .byte $F6,"LDY #",$F5,"27",13 + .byte $F6,"LDA ",$F4,"SPRITEZ",13 + .byte $F6,"AND #",$F5,"$08",13 + .byte $F6,"BEQ ",$F0,"@CONT",13 + .byte $F6,"DEY",13 + .byte $F0,"@CONT:",13 + .byte $F6,"STY ",$F5,"$900F",13,13,13 + .byte $F4,"SPRITEBACK",$F6," = " + .byte 0 + LDX CRSRCOL + STX R3 + LDY CRSRROW + STY R4 + ; + LDY #4 + LDX #18 + JSR SSSPLOT + JSR SSSPRINTS + .asciiz "ABC" + JSR HEART + JSR BALL + LDA SPRITEDEF + ORA #$40 + STA SPRITEDEF + ; +@loop: LDX #0 + JSR SSSUSE + LDX SPRITEX + INC SPRITEY + LDY SPRITEY + JSR SSSMOVEXY + JSR ANYKEY + BNE DEMO7 + LDY #27 + LDA SPRITEZ + AND #$08 + BEQ @cont + DEY +@cont: STY $900F + LDX R3 + LDY R4 + JSR SSSPLOT + LDA #0 + STA COLORCODE + LDA SPRITEBACK + JSR SSSPRINT + JMP @loop + +DEMO7: + LDA #30 + STA $900F + JSR SSSPRINTS + .byte $F4,"'GHOST MODE':",13,13 + .byte $F6,"LDA ",$F4,"SPRITEDEF",13 + .byte $F6,"ORA #",$F5,"$20",13 + .byte $F6,"STA ",$F4,"SPRITEDEF",13 + .byte 0 + LDX #7 + LDA #$FF +@fill: STA $1C08,X + DEX + BPL @fill + ; + LDA #%11101111 ; 16x16 sprite floats X-Y + LDY #$10 ; height + JSR SSSCREATE + LDA #$02 ; red + LDX #BIGRED + JSR SSSANIM + LDX #0 + LDA SSSCLIPY + LSR + TAY + JSR SSSMOVEXY + ; + LDY #9 + LDX #2 + STY R3 + STX R4 +@bar1: + LDX R4 + JSR SSSPLOT + JSR SSSPRINTS + .byte $F5,1,1,1,SSSNULL,SSSNULL,SSSNULL + .byte $F6,1,1,1,SSSNULL,SSSNULL,SSSNULL + .byte $F7,1,1,1,SSSNULL,SSSNULL,SSSNULL + .byte 0 + INC R3 + LDY R3 + CPY #14 + BNE @bar1 + +@loop: LDX #0 + JSR SSSUSE + INC SPRITEX + LDX SPRITEX + LDY SPRITEY + JSR SSSMOVEXY + JSR ANYKEY + BEQ @loop + +FINI: JMP RESET ; bye-bye! + +ANYKEY: + LDY PLAYROWS + DEY + LDX #0 + JSR SSSPLOT + JSR SSSPRINTS + .byte $F6,"-=: PRESS ANY KEY :=-" + .byte 0 + ; +@user: LDY #1 + JSR SSSFLIP + LDA $C5 + CMP #$40 + BEQ @repeat ; keypress? +@wait: LDA $C5 + CMP #$40 + BNE @wait ; release key + JSR SSSINIT ; start over + LDA #1 +@repeat: + RTS + +BALL: + LDY #10 + LDX #18 + JSR SSSPLOT + LDA #4 + STA COLORCODE + LDA #128+81 + JSR SSSCELL + LDY #0 + JSR SSSFLIP + ; + LDY #10 + LDX #18 + JSR SSSPLOT + LDA #7 + STA COLORCODE + LDA #128+87 + JSR SSSCELL + ; + RTS + +HEART: + LDA #%10001100 ; enable 8X8 float + LDY #8 + JSR SSSCREATE + ; RED HEART + LDA #2 + LDX #$98 + LDY #$82 + JSR SSSANIM + LDX #160 + LDY #96 + JSR SSSMOVEXY + LDY #0 + JSR SSSFLIP + RTS + +HEROMOVE: + LDX #0 + STX sssNUM + LDA #5 + LDX #HERO + JSR SSSANIM + LDA SPRITEX + LSR + AND #$03 + BEQ @ck0 + CMP #$02 + BEQ @r2 +@r1: + LDX #<(HERO+16) + LDY #>(HERO+16) + STX SPRITEIMGL + STY SPRITEIMGH + BNE @ck0 +@r2: + LDX #<(HERO+32) + LDY #>(HERO+32) + STX SPRITEIMGL + STY SPRITEIMGH +@ck0: ; process RIGHT + LDA R0 + CMP #-1 + BEQ @ck2 + LDA SPRITEX + SEC + SBC #8 + CMP SSSCLIPX + BEQ @ck1 + INC SPRITEX + BNE @fini +@ck1: LDA #-1 + STA R0 +@ck2: ; process LEFT + LDA R0 + AND #%00000100 + BEQ @ck3 + LDA SPRITEIMGL + CLC + ADC #$30 + BCC @lcc + INC SPRITEIMGH +@lcc: STA SPRITEIMGL + LDA SPRITEX + BEQ @ck3 + DEC SPRITEX +@fini: RTS +@ck3: LDA #0 + STA R0 + RTS + +QMANMOVE: + LDA R1 + CMP #-1 + BEQ @big + LDX #1 + STX sssNUM + LDX #QBALL + DEC SPRITEX+1 + BEQ @fini1 + LDA SPRITEX+1 + AND #$02 + BEQ @ck1 + LDX #QBALL2 +@ck1: LDA #7 + JSR SSSANIM + RTS +@fini1: LDA #-1 + STA R1 + LDA SSSCLIPX + CLC + ADC #8 + STA SPRITEX+1 + RTS +@big: + LDX #2 + STX sssNUM + LDY #>QUIKMAN + INC SPRITEX+2 + LDA SPRITEX+2 + CMP SSSCLIPX + BEQ @fini2 + AND #$03 + ASL + ASL + ASL + ASL + ASL + ADC # +; last updated: 30-Oct-2011 +; +; === IMPORTANT === +; required symbols you need to define for your game: +; +SPRITEDEF4 = $10 ; un-comment this for "repeating" flag usage +SPRITEDEF5 = $20 ; un-comment this for "ghost" flag usage +SPRITEDEF6 = $40 ; un-comment this for "collision" flag usage +SPRITEWIDE = 1 ; comment this out to skip 16-bit wide sprites +SPRITEMAX = 42 ; reserves this many SPRITE registers (1-?) +SSSNULL = $A0 ; your character used for a blank background + +;********************************************************************* +; some pertinent VIC 20 symbols +; +RNDSEED = $8B ; -$8F: BASIC RND seed value +JIFFYH = $A0 ; jiffy clock high +JIFFYM = $A1 ; jiffy clock med +JIFFYL = $A2 ; jiffy clock low +DATANEXT = $A6 ; DATASETTE pointer (0-191) +KEYCHARS = $C6 ; number of characters in KEYBUF (0-10) +RVSFLAG = $C7 ; character reverse flag +PLAYROWS = $C8 ; current screen row length (16-24) +CURSOR = $CC ; cursor enable (0=flash) +CRSRCHAR = $CE ; character under cursor +SCRNLINE = $D1 ; pointer to cursor's screen line +CRSRCOL = $D3 ; position of cursor on screen line +PLAYCOLS = $D5 ; current screen line length (16-24) +CRSRROW = $D6 ; screen row where cursor is +COLORLINE = $F3 ; pointer to cursor's color line +INPUT = $0200 ; -$0258: 89-character BASIC INPUT buffer +KEYBUF = $0277 ; -$0280: 10-character keyboard buffer +COLORCODE = $0286 ; current cursor color +CRSRCOLOR = $0287 ; color under cursor +SCRNPAGE = $0288 ; active screen memory page (unexpanded = $1E) +SHIFTMODE = $0291 ; 0=allow, 128=locked +SCROLLFLAG = $0292 ; auto scrolldown flag +ACOPY = $030C ; temp storage for A register +XCOPY = $030D ; temp storage for X register +YCOPY = $030E ; temp storage for Y register +DATASETTE = $033C ; -$03FB: 192-byte tape input buffer +MASK = $8270 ; ROM character $40 - Shift-M (\) +VIC = $9000 ; start of Video Interface Chip registers +MACHINE = $EDE4 ; NTSC=$05, PAL=$0C +STOPKEY = $F770 ; check for STOP key pressed +RESET = $FD22 ; warm startup +CHROUT = $FFD2 ; print character with cursor translation +GETIN = $FFE4 ; get a character from keyboard queue + +;********************************************************************* +; volatile VIC-SSS symbols +; +VECTORBG = $01 ; sprite temp pointer to an image source +DIRTYLINE2 = $59 ; -$70: 24 screen rows for last dirty column +1 +NEWDIRT = $BF ; bit 7=VIDEO1, 6=VIDEO2, 5=PLAYFIELD, 4=STATIC +DIRTYLINE = $D9 ; -$F0: 24 screen rows for starting dirty column +DIRTMAP = $F1 ; pointer to PLAYCOLOR for dirty-bit updates +VECTORFG = $F7 ; sprite temp pointer to an image target +VECTOR1 = $F9 ; sprite temp pointer +VECTOR2 = $FB ; sprite temp pointer +VECTOR3 = $FD ; sprite temp pointer +FPS = $0285 ; number of VIC re-directions every 64-jiffies +PENDING = $0293 ; next video page: $10 or $12 +ACTUAL = $0294 ; save VIC startup video page +VSYNC = $0295 ; set when waiting for vertical sync(s) +VSYNC2 = $0296 ; frames skipped +VCOUNT = $0297 ; current SSSFLIP count +SSSCLIPX = $0298 ; pixels to right border: 8 * (PLAYCOLS + 2) +SSSCLIPY = $0299 ; pixels to bottom border: 8 * (PLAYROWS + 2) +R0 = $029A ; unused temporary register +R1 = $029B ; unused temporary register +R2 = $029C ; unused temporary register +R3 = $029D ; unused temporary register +R4 = $029E ; unused temporary register + +;********************************************************************* +; FRAME REGISTERS +; +VICFRAME1 = $1000 ; first video buffer +VICCOLOR1 = $9400 ; first color buffer +VICFRAME2 = $1200 ; second video buffer +VICCOLOR2 = $9600 ; second color buffer +PLAYFIELD = $1400 ; write-pending screen buffer +PLAYCOLOR = $1600 ; write-pending color buffer (bits 0-3) + ; bit 4 = static cell bit, sprites go behind + ; bit 5 = dirty bit for pending page + ; bit 6 = dirty bit for video page 2 only + ; bit 7 = dirty bit for video page 1 only + +;********************************************************************* +; SPRITE REGISTERS +; +.global SSSBUF ; defaults to $1800, but can be relocated by linker +.global SPRITEBACK ; character code this sprite is in collision with +.global SPRITEBUFH ; pointer within sprite image buffer @ $1800 - $19FF +.global SPRITEBUFL +.global SPRITEC1H ; pointer within sprite display character pool +.global SPRITEC1L +.global SPRITEC2H ; pointer within sprite display character pool +.global SPRITEC2L +.global SPRITECOL ; 4-bit VIC color code +.global SPRITECX ; sprite collision X-coord +.global SPRITECY ; sprite collision Y-coord +.global SPRITEDEF ; matrix definition: + ; bit 0: height 0 = 8px; 1 = 16px + ; bit 1: width 0 = 8px; 1 = 16px + ; bit 2: float Y 0=fixed cell; 1=vertical float + ; bit 3: float X 0=fixed cell; 1=horizontal float + ; bit 4: repeat 0=independent; 1=re-use previous + ; bit 5: ghost 0=merge image; 1=invert image + ; bit 6: collision 0=ignore; 1=detect + ; bit 7: enabled 0=invisible; 1=visible +.global SPRITEH ; number of raster lines (1-16) +.global SPRITEIMGH ; pointer to source graphic for rendering at 0,0 +.global SPRITEIMGL +.global SPRITEX ; horizontal pixel coordinate, visible >0 - 0 - ', $BE +.charmap '?', $BF +.charmap '~', $DE ; PI symbol + diff --git a/sprite invaders/VIC-SSS-MMX.s b/sprite invaders/VIC-SSS-MMX.s new file mode 100755 index 0000000..21b8bd4 --- /dev/null +++ b/sprite invaders/VIC-SSS-MMX.s @@ -0,0 +1,1438 @@ +;********************************************************************* +; Commodore VIC 20 Software Sprite Stack - MMX Edition +; last updated: 22-Sep-2011 +; written by Robert Hurst +; with contributions from: +; Matt Dawson +;********************************************************************* + + .fileopt author, "Robert Hurst" + .fileopt comment, "Software Sprite Stack" + .fileopt compiler, "VIC 20 ASSEMBLER" + + +;********************************************************************* +; To assemble this source using cc65.org project: +; ca65.exe --cpu 6502 --listing VIC-SSS-MMX.s +; Then link it into your project: +; ld65.exe -C yourlinker.cfg -o yourgame.prg yourgame.o VIC-SSS-MMX.o +; +; See the various .bat files used for working examples. +; + .include "VIC-SSS-MMX.h" + .segment "SPRITE" + + +;********************************************************************* +; Software Sprite Stack INITIALIZATION +; +; MUST BE INVOKED ONCE BEFORE USING ANY OTHER SSS CALL +; Value in COLORCODE will be used to fill the color buffers. +; +SSSINIT: + ; SSS geometry + LDA VIC+$02 + AND #$1F + STA PLAYCOLS + ASL ; CLC + ADC #$04 + ASL + ASL + STA SSSCLIPX + LDA VIC+$03 + AND #$7E + LSR + STA PLAYROWS + ASL ; CLC + ADC #$04 + ASL + ASL + STA SSSCLIPY + ; + LDA #$00 + STA CRSRROW + STA CRSRCOL + TAY + TAX +@sss: TYA + STA sss+1,X + LDA CRSRCOL + STA sss,X + CLC + ADC PLAYCOLS + BCC @cc + INY +@cc: STA CRSRCOL + INX + INX + INC CRSRROW + LDA CRSRROW + CMP PLAYROWS + BNE @sss + ; + ; SSS active / pending video page pointers + LDA #>VICFRAME1 + STA SCRNPAGE + LDA #>VICFRAME2 + STA PENDING + LDA #$40 + STA NEWDIRT + ; + ; kernal init + LDA #$01 + STA RVSFLAG ; character reverse flag + LDA #$80 + STA SHIFTMODE ; locked + LDA #$00 + STA SCROLLFLAG ; disable + ; + ; SPRITE register init + STA SPRITES + ; + ; fill VIC screen / color buffers + LDA #SSSNULL + JSR SSSCLEAR + ; + ; VIC register init + LDA VIC+$02 + AND #$7F ; if $80 enabled, +$0200 to base screen address + STA VIC+$02 + LDA #$CF ; point VIC screen @ $1000 w/ char set @ $1C00 + ; LDA #$C0 ; uncomment for debugging display purposes + STA VIC+$05 + ; + ; FRAME register init + LDY #$00 + STY FPS + STY VSYNC + STY VSYNC2 + STY VCOUNT + JSR SSSFLIP + RTS + + +;********************************************************************* +; Software Sprite Stack IRQ HANDLER +; +; PROGRAM USE OF THIS HANDLER IS OPTIONAL +; +; customize to your liking, i.e., +; - change JMP $EABF here to continue to your custom IRQ handler; or +; - JMP SSSIRQ at the end of your IRQ handler. +; +SSSIRQ: + LDA JIFFYL + AND #%00111111 + BNE @cont + LDX VCOUNT + STX FPS + STA VCOUNT +@cont: + INC VSYNC2 ; frame skipped? + LDA VSYNC + BEQ @fskip ; program is NOT waiting ... + LDX #$00 + SEC + SBC VSYNC2 + STA VSYNC ; save result + BCS @reset ; program wants more than 1 screen refresh + STX VSYNC ; clear wait for vertical sync flag +@reset: STX VSYNC2 ; clear frame skip counter +@fskip: + JMP $EABF + + +;********************************************************************* +; Software Sprite Stack CLEAR SCREEN +; +; Pass A with the character code to fill the PLAYFIELD buffer. +; Value in COLORCODE will be used to fill the PLAYCOLOR buffer. +; +; These changes will go into effect only after a call to SSSFLIP +; +SSSCLEAR: + PHA ;++ save character fill code + LDX #$00 + LDY #$00 + JSR SSSPLOT ; home "cursor" +@reset: TYA + STA DIRTYLINE,X + LDA PLAYCOLS + STA DIRTYLINE2,X + INX + CPX PLAYROWS + BNE @reset + ; +@cls: PLA ;-- + PHA ;++ + JSR SSSPRINT + LDX CRSRCOL + BNE @cls ; loop until column wraps + LDY CRSRROW + BNE @cls ; loop until row wraps, too + PLA ;-- + RTS + + +;********************************************************************* +; Software Sprite Stack PLOT CURSOR IN FRAME BUFFER +; +; SSSPLOT uses PLAYFIELD frame buffer. +; SSSPLOTS uses PENDING frame buffer. +; +; Pass X/Y with coordinate to put cursor. +; +SSSPLOT: + LDA #>PLAYFIELD + STA SCRNLINE+1 + LDA #>PLAYCOLOR + STA COLORLINE+1 + BNE SSSPLOTX +; +; PENDING frame cursor (for writing SPRITE chars) +SSSPLOTS: + LDA PENDING + STA SCRNLINE+1 + ORA #$84 + STA COLORLINE+1 +; +SSSPLOTX: + LDA #>PLAYCOLOR + STA DIRTMAP+1 +@x: CPX PLAYCOLS + BMI @y + LDX PLAYCOLS + DEX +@y: CPY PLAYROWS + BMI @ok + LDY PLAYROWS + DEY +@ok: STX CRSRCOL ; maintain column offset to row + STY CRSRROW ; maintain row number + TYA + ASL + TAY + LDA sss+1,Y + BEQ @top + INC SCRNLINE+1 + INC COLORLINE+1 + INC DIRTMAP+1 +@top: LDA sss,Y + STA SCRNLINE + STA COLORLINE + STA DIRTMAP + LDY CRSRROW + RTS + + +;********************************************************************* +; Software Sprite Stack PRINT TO A FRAME BUFFER +; +; Like SSSPOKE, writes to current cursor, but also advances it to +; the right (with line/screen wrap) upon completion. +; +; All registers are preserved from this call, for common loop use. +; +SSSPRINT: + STX XCOPY ; save index registers + STY YCOPY + JSR SSSPOKE + INC CRSRCOL ; cursor right + LDA CRSRCOL + CMP PLAYCOLS ; moved past right edge? + BCC @fini ; no, all done + LDA #$00 + STA CRSRCOL ; reset to 1st column + INC CRSRROW ; and advance down a row + LDA CRSRROW + CMP PLAYROWS ; moved past bottom edge? + BCS @toprow ; yes, wrap back to top + LDA SCRNLINE ; no, re-calculate new row pointer + CLC + ADC PLAYCOLS + BCC @cc + INC SCRNLINE+1 + INC COLORLINE+1 +@cc: STA SCRNLINE + STA COLORLINE +@fini: LDY CRSRCOL + LDA (COLORLINE),Y + STA CRSRCOLOR + LDA (SCRNLINE),Y + STA CRSRCHAR + LDX XCOPY ; restore index registers + LDY YCOPY + RTS +@toprow: + LDA #$00 + STA CRSRROW + STA SCRNLINE + STA COLORLINE + DEC SCRNLINE+1 + DEC COLORLINE+1 + BNE @fini + + +;********************************************************************* +; Software Sprite Stack PRINT STRING FROM POINTER ON STACK +; +; Will print bytes, until a NULL, following the JSR call here. +; Carriage control and color codes are interpreted. +; +SSSPRINTS: + PLA + STA VECTORFG + PLA + STA VECTORFG+1 + LDY #$01 +@loop: LDA (VECTORFG),Y + BEQ @fini + CMP #$0D + BNE @ctrl + TYA + PHA + LDY CRSRROW + INY + LDX #0 + JSR SSSPLOT + PLA + TAY + JMP @next +@ctrl: CMP #$C0 + BCC @cont + CMP #$D0 + BCS @cont ; color code? + AND #$0F ; filter for 16-colors + STA COLORCODE ; 0=blk,1=wht,2=red,3=cyn,4=mag,5=grn,6=blu,7=yel + JMP @next +@cont: JSR SSSPRINT +@next: INY + BNE @loop + INC VECTORFG+1 + BNE @loop + ; +@fini: TYA + CLC + ADC VECTORFG + BCC @cc + INC VECTORFG+1 +@cc: STA VECTORFG + LDA VECTORFG+1 + PHA + LDA VECTORFG + PHA + RTS + + +;********************************************************************* +; Software Sprite Stack READ FROM A FRAME BUFFER +; +; SSSPEEK reads from PLAYFIELD frame buffer. +; SSSPEEKS reads from PENDING frame buffer. +; SSSPEEKXY reads from PLAYFIELD frame buffer, using the sprite pixel +; coordinate system to determine X/Y cursor positioning. +; +; Pass X/Y with the coordinate to put cursor. +; CRSRCHAR and CRSRCOLOR are filled with values under cursor, with +; the former returned in Accumulator. +; +SSSPEEKS: + JSR SSSPLOTS + JMP SSSPEEKX +; +; pixel X/Y +SSSPEEKXY: + CPX #$10 + BCC @fini ; hidden by left border + CPX SSSCLIPX + BCS @fini ; hidden by right border + CPY #$10 + BCC @fini ; above top border + CPY SSSCLIPY + BCC @ok ; below bottom border +@fini: RTS +@ok: TYA + SEC + SBC #$10 + LSR + LSR + LSR + TAY + ; + TXA + SEC + SBC #$10 + LSR + LSR + LSR + TAX +; +SSSPEEK: + JSR SSSPLOT +; +SSSPEEKX: + LDY CRSRCOL + LDA (COLORLINE),Y + STA CRSRCOLOR + LDA (SCRNLINE),Y + STA CRSRCHAR + RTS + + +;********************************************************************* +; Software Sprite Stack WRITE TO A FRAME BUFFER +; +; Pass Accumulator with character code to write to current cursor. +; COLORCODE is used to fill same space with that value. +; +SSSPOKE: + LDY CRSRCOL + STA (SCRNLINE),Y + LDA (DIRTMAP),Y + LDX SCRNLINE+1 + CPX #>PLAYFIELD + BPL @bg + ORA NEWDIRT + STA (DIRTMAP),Y ; update sprite's dirty bits only + .ifdef SPRITEDEF5 + LDA (COLORLINE),Y + BIT COLORCODE + BMI @color ; keep color in place + .endif + LDA COLORCODE ; add color directly to map + JMP @color +@bg: AND #%11000000 ; keep any sprite dirt + ORA #%00100000 ; dirty this PLAYFIELD cell + ORA COLORCODE ; add color +@color: STA (COLORLINE),Y + LDX CRSRROW + LDY CRSRCOL + LDA DIRTYLINE,X + CMP CRSRCOL + BCC @ok ; is dirty seek lower than this write? + STY DIRTYLINE,X ; start looking for dirty cells from here +@ok: TYA + CMP DIRTYLINE2,X + BCC @ok2 ; is dirty seek higher than this write? + INY + STY DIRTYLINE2,X ; stop looking for dirty cells after here +@ok2: RTS + + +;********************************************************************* +; Software Sprite Stack FLIP ACTIVE / PENDING FRAME BUFFERS +; +; Pass Y with the number of vertical sync counts to wait for, or zero +; to make changes visible immediately. +; +; Only the current cursor position is preserved when completed. +; +; If user is holding RUN/STOP key down, the current video frame is +; paused until it is released. +; +SSSFFLIP: + LDA #$00 ; gameplay needs its action to move faster + BIT VSYNC2 + BPL SSSFLIP2 ; do we need to drop a frame? + STA VSYNC2 + RTS + ; +SSSFLIP: + LDA #$00 + STA VSYNC2 +SSSFLIP2: + LDA CRSRROW + PHA ;++ save row + LDA CRSRCOL + PHA ;++ save column + TYA + PHA ;++ save Y (frame count) + ; + ; Phase I: + ; - write any new PLAYFIELD updates to PENDING frame + ; - erase any SPRITE characters from PENDING frame + ; + LDA #%00010000 ; clean TOPFIELD dirt only after this update + JSR SSSCOMMIT + ; + ; Phase II: + ; - render & write SPRITE characters to PENDING frame + ; + JSR SSSUPDATE + ; + ; PHASE III: + ; - signal IRQ to flip video to PENDING frame + ; - wait for the all clear + ; + PLA ;-- restore A (frame count) + TAY + INY ; account for a 'missed' frame + ; INY ; allow for another 'missed' frame + CPY VSYNC2 + BCS @pace ; is game loop & rendering ok? + LDY #$80 ; nope, flag next call to SSSFFLIP + STY VSYNC2 ; to skip rendering/flip altogether + LDA #$00 ; and don't wait for this vsync either +@pace: STA VSYNC ; enable screen to flip +@vsync: LDA VSYNC + BNE @vsync ; and wait for it to occur + BIT VSYNC2 + BMI @ok + STA VSYNC2 ; fast flip, gauge again for next frame + ; +@ok: LDA VIC+$02 + EOR #$80 + STA VIC+$02 ; re-direct VIC to other screen buffer + INC VCOUNT + LDA SCRNPAGE + STA PENDING ; make active screen as pending + EOR #$02 + STA SCRNPAGE ; maintain VIC active video page + ; + ; PHASE IV: + ; - write the same PLAYFIELD updates to new PENDING frame + ; + LDA #%00100000 ; clean PLAYFIELD dirt as part of this update + JSR SSSCOMMIT + ; + PLA ;-- restore X (column) + TAX + PLA ;-- restore Y (row) + TAY + JSR SSSPLOT + ; +@pause: JSR STOPKEY + BNE @fini + LDA #$00 ; clear skipped frame count + STA VSYNC2 + STA VCOUNT + ; === write any custom PAUSE or RESET code here === + PLA + PLA + .global RESTART + JMP RESTART ; label speaks for itself + ; +@fini: RTS + + +;********************************************************************* +; Software Sprite Stack COMMIT CHANGES TO PENDING FRAME BUFFER +; +; pass Accumulator with the PLAYFIELD bit(s) set for cleaning: +; - bit 7 for video #1 +; - bit 6 for video #2 +; - bit 5 for playfield +; - bit 4 for topfield +; +; SPRITE dirt for the PENDING frame is always cleaned. +; +; This is used by SSSFLIP and NOT normally called by user programs. +; +DIRTYMASK = $00 +CLEANER = $01 ; bit 7=VIDEO1, 6=VIDEO2, 5=PLAYFIELD, 4=STATIC +; +SSSCOMMIT: + LDX PENDING + CPX #>VICFRAME1 ; will flip to video #1 next? + BNE @scrn2 + ORA #%10000000 ; erase old sprites from video #1 + BNE @cont +@scrn2: ORA #%01000000 ; erase old sprites from video #2 +@cont: TAY + EOR #$FF ; reverse check to clean + STA CLEANER + TYA + ORA #%00100000 ; but always look for PLAYFIELD dirt + STA DIRTYMASK + TXA + STA VECTOR2+1 + ORA #$84 ; and its COLOR + STA VECTOR3+1 + LDX #$00 + STX VECTOR2 + STX VECTOR3 + LDY #$00 + JSR SSSPLOT ; home "cursor" + ; + LDX #$00 +@forx: LDA DIRTYLINE2,X + STA ACOPY + LDA DIRTYLINE,X ; dirty start column for this row + CMP ACOPY + BCS @nextx ; reached end of this line? + LDY PLAYCOLS + STY NEWDIRT + STY DIRTYLINE,X ; reset this line's seek for next commit + LDY #$00 + STY DIRTYLINE2,X ; reset this line's end for next commit + TAY ; but start this commit from this column +@fory: LDA (COLORLINE),Y + AND #%11100000 ; is this cell dirty for ANY update? + BEQ @nexty ; no, skip it + STY DIRTYLINE2,X ; new ending column for this line + INC DIRTYLINE2,X ; new ending column for this line + CPY NEWDIRT + BCS @more + STY NEWDIRT + STY DIRTYLINE,X ; new starting column for this line +@more: AND DIRTYMASK ; is this cell dirty for THIS update? + BEQ @nexty ; no, skip it + LDA (COLORLINE),Y + STA (VECTOR3),Y ; update color cell + AND CLEANER ; remove this dirt from this cell + STA (COLORLINE),Y + LDA (SCRNLINE),Y + STA (VECTOR2),Y ; update video cell +@nexty: INY + CPY ACOPY + BCC @fory +@nextx: LDA SCRNLINE + CLC + ADC PLAYCOLS + BCC @cc + INC SCRNLINE+1 + INC COLORLINE+1 + INC VECTOR2+1 + INC VECTOR3+1 +@cc: STA SCRNLINE + STA COLORLINE + STA VECTOR2 + STA VECTOR3 + INX + CPX PLAYROWS + BCC @forx + ; + LDA #%10000000 + LDX PENDING + CPX #>VICFRAME2 ; will flip to video #2 next? + BNE @scrn1 + LDA #%01000000 +@scrn1: STA NEWDIRT + RTS + + +;********************************************************************* +; Software Sprite Stack CREATE A NEW SPRITE IN THE LIST +; +; pass Accumulator with the SPRITEDEF value (see HEADER) +; pass Y with the SPRITEH value (1-16) +; returns X with sprite index #0 thru SPRITEMAX-1 +; +SSSCREATE: + STY YCOPY + LDX SPRITES + CPX #SPRITEMAX + BCC @cont + RTS ; sorry, increase SPRITEMAX and re-compile +@cont: + STA SPRITEDEF,X + CPX #$00 + BNE @append ; >1 sprite + ; + ; this is the first sprite in the list ... + LDA #SSSBUF + STA SPRITEBUFL + STY SPRITEBUFH + LDY #$20 ; start at top of custom character + STX SPRITEC1L + STY SPRITEC1H + STX SPRITEC2L + STY SPRITEC2H + BNE @compute +@append: + ; copy prior sprite vectors + LDA SPRITEBUFL-1,X + STA SPRITEBUFL,X + LDA SPRITEBUFH-1,X + STA SPRITEBUFH,X + LDA SPRITEC1L-1,X + STA SPRITEC2L,X + LDA SPRITEC1H-1,X + STA SPRITEC1H,X + STA SPRITEC2H,X + .ifdef SPRITEDEF4 + ; repeating sprite? + LDA SPRITEDEF,X + AND #SPRITEDEF4 + BNE @same + .endif + ; no, allocate new image and character buffers + LDA SPRITEDEF-1,X + AND #$0F + TAY + LDA sssALLOC,Y + CLC + ADC SPRITEBUFL,X + BCC @cc + INC SPRITEBUFH,X +@cc: STA SPRITEBUFL,X +@compute: + LDA SPRITEDEF,X + AND #$0F + TAY + LDA sssALLOC,Y + STA sssBYTES + ; vector#2 into custom char set + LDA SPRITEC2L,X + SEC + SBC sssBYTES ; account for its entire buffer size + BCS @cc1 + DEC SPRITEC2H,X + DEC SPRITEC1H,X +@cc1: STA SPRITEC2L,X + STA SPRITEC1L,X + ; vector#1 into custom char set + SEC + SBC sssBYTES ; account for its entire buffer size + BCS @cc2 + DEC SPRITEC1H,X +@cc2: STA SPRITEC1L,X + JMP @new + ; keep repeating sprite pointing to same custom characters +@same: LDA SPRITEC1L-1,X + STA SPRITEC1L,X + LDA SPRITEC2L-1,X + STA SPRITEC2L,X + LDA SPRITEC2H-1,X + STA SPRITEC2H,X +@new: LDA YCOPY + STA SPRITEH,X + .ifdef SPRITEDEF6 + LDA #SSSNULL ; init with nothing in contact + STA SPRITEBACK,X + .endif + LDA #$00 + .ifdef SPRITEDEF6 + STA SPRITECX,X ; init collision X-coord + STA SPRITECY,X ; init collision Y-coord + .endif + STA SPRITEX,X ; sprite is not in visible area to start + STA SPRITEY,X + STA SPRITEZ,X ; all flags off + LDX SPRITES ; return this new sprite # as initialized + STX sssNUM + INC SPRITES ; account for the new sprite allocated + RTS + + +;********************************************************************* +; Software Sprite Stack SELECT A SPRITE TO MANIPULATE +; +; pass X index with the SPRITES number (0 - = outside right border visible range + LDA SPRITEY,X + BEQ @redraw ; 0 = outside top border visible range + CMP SSSCLIPY + BCS @redraw ; >= outside bottom border visible range + .endif + + ; preset sprite image buffer: + ; VECTOR1 = pointer to top-left within sprite matrix + ; VECTOR2&3 = pointer to adjacent chars, as necessary +@own: ; + JSR SSSUSE + LDA SPRITEBUFH,X + STA VECTOR1+1 + STA VECTOR2+1 + + .ifdef SPRITEWIDE + STA VECTOR3+1 + .endif + + LDA SPRITEBUFL,X + STA VECTOR1 + CLC + ADC sssNEXT + BCC @cc1 + INC VECTOR2+1 + + .ifdef SPRITEWIDE + INC VECTOR3+1 + .endif + +@cc1: STA VECTOR2 + + .ifdef SPRITEWIDE + CLC + ADC sssNEXT + BCC @cc2 + INC VECTOR3+1 +@cc2: STA VECTOR3 + .endif + + .ifdef SPRITEDEF5 + LDY #$11 ; ORA opcode + LDA SPRITEDEF,X + AND #SPRITEDEF5 ; ghost image? + BEQ @bit + LDY #$51 ; EOR opcode +@bit: TYA + STA @OP1 + STA @OP2 + EOR #$40 ; swap opcode + STA @OP3 + .endif + + ; branch on make control flags + LDA SPRITEZ,X + ASL + PHA + BCC @copy ; $80 - (re)make buffered image + JSR @Make +@copy: PLA + ASL + BCC @matrix ; $40 - copy buffered image + JSR @Copy +@matrix: + JSR @Display ; display sprite matrix + JMP @loop +@redraw: + LDA SPRITEZ,X + AND #%11 + ORA #%11110000 ; make + copy/merge + null bg + clipped fg + STA SPRITEZ,X +@loop: + INC sssNUM + LDX sssNUM + JMP @do + ; + ; INIT PHASE + ; ---------- + ; (re)make this sprite's image buffer +@Make: ; + LDA SPRITEH,X + STA sssXFER ; sprite image raster count + LDA SPRITEY,X + AND #$07 + CLC + ADC sssXFER + STA sssDY ; 1st raster below image + ; + ; VECTORBG = pointer to your compact source image + LDA SPRITEIMGL,X + STA VECTORBG + LDA SPRITEIMGH,X + STA VECTORBG+1 + LDA sssNEXT + STA sssLINENUM ; this many raster lines to copy + CMP sssDY + BCS @Mloop ; fits within height of sprite + LDA sssDY + SEC + SBC sssLINENUM + STA ACOPY ; compute how many rasters to clip + LDA sssXFER + SBC ACOPY + STA sssXFER ; clip image within sprite height +@Mloop: + LDA #$00 ; erase raster registers + STA sssROR1 + STA sssROR2 + + .ifdef SPRITEWIDE + STA sssROR3 + .endif + + DEC sssLINENUM + LDY sssLINENUM + LDA sssXFER + BEQ @Mcopy ; no more rasters to copy - zero them + CPY sssDY + BCS @Mcopy ; below sprite image - zero this raster +@Mxfer: + DEC sssXFER ; copying sprite image rasters + LDY sssXFER + LDA (VECTORBG),Y + STA sssROR1 + LDA SPRITEDEF,X + AND #%00000010 + BEQ @Mxfer2 ; 16w ? + LDY #$08 + LDA SPRITEDEF,X + LSR + BCC @Monly8 ; 16h ? + LDY #$10 +@Monly8: + TYA + CLC + ADC sssXFER + TAY + LDA (VECTORBG),Y + STA sssROR2 ; load adjacent register +@Mxfer2: + LDA SPRITEX,X + AND #$07 + BEQ @Mcopy + STA sssDX +@Mx2: LSR sssROR1 + ROR sssROR2 ; shift into image overflow register #1 + + .ifdef SPRITEWIDE + ROR sssROR3 ; shift into image overflow register #2 + .endif + + DEC sssDX + BNE @Mx2 +@Mcopy: + LDX #$01 + LDY sssLINENUM + LDA sssROR1 + STA (VECTOR1),Y + CPX sssX + BEQ @Mnext + LDA sssROR2 ; write image overflow register #1 + STA (VECTOR2),Y + + .ifdef SPRITEWIDE + INX + CPX sssX + BEQ @Mnext + LDA sssROR3 ; write image overflow register #2 + STA (VECTOR3),Y + .endif + +@Mnext: + LDX sssNUM + LDA sssLINENUM + BEQ @Mfini + JMP @Mloop +@Mfini: RTS + ; + ; PHASE II + ; -------- + ; copy/merge buffered image with background into + ; sprite character matrix +@Copy: ; + LDA SPRITEZ,X + AND #%11 + EOR #%1 ; flip to other character set + STA SPRITEZ,X + AND #%1 + BNE @Cfb2 + LDA SPRITEC1L,X + STA VECTORFG + LDA SPRITEC1H,X + STA VECTORFG+1 + BNE @Ccopy +@Cfb2: LDA SPRITEC2L,X + STA VECTORFG + LDA SPRITEC2H,X + STA VECTORFG+1 +@Ccopy: + LDA SPRITEX,X + STA sssDX + LDA sssNEXT + STA sssLINE + LDY #0 + STY sssCHAR + STY sssLINENUM + STY ACOPY +@Cdocol: + LDX sssNUM + LDA SPRITEY,X + STA sssDY +@Cbgimage: + LDX sssNUM + LDA SPRITEZ,X + AND #%10 + BNE @Cjmp ; fast copy? + LDX sssDX + LDY sssDY + JSR SSSREAD + CMP #SSSNULL + BNE @Cmore +@Cjmp: JMP @Ccpfast +@Cmore: + LDX sssNUM + LDA CRSRCOLOR + AND #%10000 + BEQ @Ccont ; static cell? + LDA SPRITEZ,X + ORA #%10000 ; flag that sprite's foreground is clipped + STA SPRITEZ,X + JMP @Cnextrow ; don't bother merging with a background +@Ccont: + + .ifdef SPRITEDEF4 + LDA SPRITEDEF,X + AND #SPRITEDEF4 + BNE @Cjmp + .endif + + LDA CRSRCHAR + JSR SSSIMAGE + STX VECTORBG + STY VECTORBG+1 + LDX #0 + STX sssXFER + INC sssCHAR ; flag that there is something behind this sprite + + .ifdef SPRITEDEF6 + LDY sssNUM + LDA SPRITEZ,Y + AND #%1000 + BNE @Ccploop ; already a collision? + LDA SPRITEDEF,Y + AND #SPRITEDEF6 + BNE @Ccploopx ; collision detection enabled? + .endif + +@Ccploop: + LDY sssXFER ; from ... + LDA (VECTORBG),Y + LDY sssLINENUM ; to ... +@OP1: ORA (VECTOR1),Y ; opcode modification #1: ORA / EOR + STA (VECTORFG),Y +@Ccpnxt: + INC sssLINENUM + INC sssXFER + INX + CPX #8 + BNE @Ccploop + BEQ @Cnextrow + + .ifdef SPRITEDEF6 +@Ccploopx: + LDY sssXFER ; from ... + LDA (VECTORBG),Y + PHA ;++ + LDY sssLINENUM ; to ... +@OP2: ORA (VECTOR1),Y ; opcode modification #2: ORA / EOR + STA (VECTORFG),Y + PLA ;-- +@OP3: EOR (VECTOR1),Y ; opcode modification #3: ORA / EOR + CMP (VECTORFG),Y + BEQ @Cnohit ; any overlapping pixel(s)? + LDY sssNUM + LDA sssDX + STA SPRITECX,Y ; save X sprite coord of what was hit + LDA sssDY + STA SPRITECY,Y ; save Y sprite coord of what was hit + LDA CRSRCHAR ; save character code of what was hit + STA SPRITEBACK,Y + LDA SPRITEZ,Y + ORA #%1000 ; sprite-pixel collision with non-static cell + STA SPRITEZ,Y + BNE @Ccpnxt ; resume normal copy/merge operation +@Cnohit: + INC sssLINENUM + INC sssXFER + INX + CPX #8 + BNE @Ccploopx + BEQ @Cnextrow + .endif + + ; faster copy, because there is no backgound to merge with ... +@Ccpfast: + LDX #8 + LDY sssLINENUM +@Ccploop2: + LDA (VECTOR1),Y + STA (VECTORFG),Y + INY + DEX + BNE @Ccploop2 + STY sssLINENUM +@Cnextrow: + LDA sssDY + CLC + ADC #8 + STA sssDY + LDY sssLINENUM + CPY sssLINE + BEQ @Cnextcol + JMP @Cbgimage +@Cnextcol: + LDA ACOPY + CLC + ADC sssNEXT + STA ACOPY + STA sssLINENUM + LDA sssLINE + CLC + ADC sssNEXT + STA sssLINE + LDA sssDX + CLC + ADC #$08 + STA sssDX + DEC sssX + BEQ @Cdone + JMP @Cdocol +@Cdone: + LDA sssCHAR + BNE @Cfini + LDX sssNUM ; all null background, if no new changes occur + LDA SPRITEZ,X ; don't do a merge on next flip either + ORA #$20 ; enable this sprite to be re-used as-is + STA SPRITEZ,X ; on next frame flip +@Cfini: RTS + ; + ; PHASE III + ; --------- + ; display sprite character matrix + ; by row, then by column +@Display: + LDX sssNUM + + .ifdef SPRITEDEF4 + LDA SPRITEDEF,X + ASL + BCC @Cfini ; sprite is disabled + .endif + + JSR SSSUSE + DEC sssY + DEC sssX + LDA SPRITEX,X + STA sssDX + LDA SPRITECOL,X + STA COLORCODE + + .ifdef SPRITEDEF5 + LDA SPRITEDEF,X + AND #SPRITEDEF5 ; ghost mode? + BEQ @Dok + LDA COLORCODE + ORA #$80 + STA COLORCODE ; flag to keep PLAYFIELD colored cells + .endif + +@Dok: LDA SPRITEZ,X + AND #%1 + BNE @Dfb2 ; which character set to use? + LDA SPRITEC1L,X + STA VECTORFG + LDA SPRITEC1H,X + BNE @Dchar +@Dfb2: LDA SPRITEC2L,X + STA VECTORFG + LDA SPRITEC2H,X +@Dchar: STA VECTORFG+1 + SEC + SBC #$1C ; starting page of custom chars + ASL + ASL + ASL + ASL + ASL ; x32 + STA sssCHAR + LDA VECTORFG + LSR + LSR + LSR ; /8 + ADC sssCHAR + STA sssCHAR ; start with this custom character +@Dcol: + LDA sssY + STA sssLINENUM + LDX sssNUM + LDA SPRITEY,X + STA sssDY ; custom character row (0, 1?, 2?) +@Drow: + LDA sssCHAR + LDX sssDX + LDY sssDY + JSR SSSWRITE ; display it +@Dskip: + INC sssCHAR ; account for it, even if it is not displayed + LDA sssLINENUM + BEQ @Dnrow + DEC sssLINENUM + LDA sssDY + CLC + ADC #8 + STA sssDY ; next Y-pixel + JMP @Drow +@Dnrow: + LDA sssDX + CLC + ADC #8 + STA sssDX + LDA sssX + BEQ @Dncol + DEC sssX + JMP @Dcol +@Dncol: + RTS + + +;********************************************************************* +; Software Sprite Stack GET IMAGE ADDRESS FROM A CHARACTER +; +; This is used by SSSUPDATE and NOT normally called by user programs. +; +; Pass A with the character code. +; returns X/Y as a pointer to its image source. +; +SSSIMAGE: + TAY + ASL + ASL + ASL ; x8 + TAX ; save image low byte + TYA + ROL ; set carry bit + LDY #$00 ; point to custom chars + BCC @cont ; is character reversed? + INY +@cont: ROL + ROL + ROL + AND #%00000011 + ORA @vic,Y ; prepend page pointer + TAY ; save image high byte + RTS + ; VIC custom or ROM characters +@vic: .byte $1C, $80 + + +;********************************************************************* +; Software Sprite Stack READ FROM PENDING FRAME BUFFER +; +; This is used by SSSUPDATE and NOT normally called by user programs. +; +; Pass X/Y with a sprite pixel coordinate. +; CURSOR is re-plotted to this location. +; returns Accumulator with character code from PENDING frame buffer, +; or a SPACE if the coordinate is outside the screen borders. +; returns X/Y cell coordinates. +; +SSSREAD: + LDA #SSSNULL ; default to an empty background + CPX #$10 + BCC @fini ; hidden by left border + CPX SSSCLIPX + BCS @fini ; hidden by right border + CPY #$10 + BCC @fini ; above top border + CPY SSSCLIPY + BCS @fini ; below bottom border + ; + LDA PENDING + STA SCRNLINE+1 + LDA #>PLAYCOLOR + STA COLORLINE+1 + ; + TYA + SEC + SBC #$10 + LSR + LSR + LSR ; /8 + TAY + STY CRSRROW + ; + TXA + SEC + SBC #$10 + LSR + LSR + LSR ; /8 + STA CRSRCOL + ; +@ok: TYA + ASL + TAY + LDA sss+1,Y + BEQ @top + INC SCRNLINE+1 + INC COLORLINE+1 +@top: LDA sss,Y + STA SCRNLINE + STA COLORLINE + LDY CRSRCOL + LDA (COLORLINE),Y ; read from pending color buffer + STA CRSRCOLOR + LDA (SCRNLINE),Y ; read from pending video buffer + STA CRSRCHAR + LDY CRSRROW +@fini: RTS + + +;********************************************************************* +; Software Sprite Stack WRITE TO PENDING FRAME BUFFER +; +; This is used by SSSUPDATE and NOT normally called by user programs. +; +; Pass X/Y with a sprite pixel coordinate. +; Pass A with SPRITE character code to write to PENDING screen buffer. +; COLORCODE is used to fill same space with that value. +; +; Write does not occur if X/Y lie outside the screen borders. +; +SSSWRITE: + CPX #$10 + BCC @fini ; hidden by left border + CPX SSSCLIPX + BCS @fini ; hidden by right border + CPY #$10 + BCC @fini ; above top border + CPY SSSCLIPY + BCS @fini ; below bottom border + PHA ;++ + JSR SSSPEEKXY + LDA CRSRCOLOR + AND #$10 ; static cell? + BEQ @ok + PLA ; yes, don't overwrite it! + RTS + ; +@ok: LDX CRSRCOL + LDY CRSRROW + JSR SSSPLOTS + PLA ;-- + JSR SSSPOKE +@fini: RTS + + +;********************************************************************* +; Software Sprite Stack PROTECTED WRITE TO PENDING FRAME BUFFER +; +; Pass X/Y with a screen cell coordinate. +; Pass A with character code to write to PENDING screen buffer. +; COLORCODE is used to fill same space with that value. +; +SSSCELL: + PHA ;++ + LDA NEWDIRT + STA $01 + LDA #$10 + STA NEWDIRT + JSR SSSPLOTS + PLA ;-- + JSR SSSPOKE + LDA $01 + STA NEWDIRT + RTS + diff --git a/sprite invaders/basic+8k.cfg b/sprite invaders/basic+8k.cfg new file mode 100755 index 0000000..4bb95f6 --- /dev/null +++ b/sprite invaders/basic+8k.cfg @@ -0,0 +1,31 @@ +# VIC 20 BASIC startup, with at least 8k expansion + +MEMORY { + ZP: start = $0000, size = $0100, type = rw; + RAM0: start = $0400, size = $0C00, type = rw; + RAM: start = $11FF, size = $0201, type = rw, fill = yes; + SCR: start = $1400, size = $0400, type = ro, fill = yes; + BUF: start = $1800, size = $0400, type = rw, fill = yes; + CHAR: start = $1C00, size = $0400, type = rw, fill = yes; + RAM1: start = $2000, size = $2000, type = rw; + RAM2: start = $4000, size = $2000, type = rw; + RAM3: start = $6000, size = $2000, type = rw; + ROM1: start = $A000, size = $1000, type = ro; + ROM2: start = $B000, size = $1000, type = ro; +} + +SEGMENTS { + BASIC: load = RAM, type = ro, define = yes, optional = no; + STARTUP: load = RAM, type = ro, define = yes, optional = no; + SPLASH: load = SCR, type = ro, define = yes, optional = yes; + SSSBUF: load = BUF, type = rw, define = yes, optional = no; + MYCHAR: load = CHAR, type = rw, define = yes, optional = no; + CODE: load = RAM1, type = ro, define = yes, optional = no; + SPRITE: load = RAM1, type = ro, define = yes, optional = no; + RODATA: load = RAM1, type = ro, define = yes, optional = no; +} + +# with at least 8K memory expansion, start here ... +FEATURES { + STARTADDRESS: default = $11FF; +} diff --git a/sprite invaders/basic.s b/sprite invaders/basic.s new file mode 100755 index 0000000..f097a4d --- /dev/null +++ b/sprite invaders/basic.s @@ -0,0 +1,606 @@ +;********************************************************************* +; COMMODORE VIC 20 BOOT USING BASIC 2.0 +; written by Robert Hurst +; updated version: 30-Oct-2011 +; + .fileopt author, "Robert Hurst" + .fileopt comment, "Sprite Invaders" + .fileopt compiler, "VIC 20 ASSEMBLER" + + .include "VIC-SSS-MMX.h" + +;********************************************************************* +; Commodore BASIC 2.0 program +; +; LOAD "YOUR PROGRAM.PRG",8 +; RUN +; + .segment "BASIC" + + .word RUN ; load address +RUN: .word @end ; next line link + .word 2011 ; line number + .byte $9E ; BASIC token: SYS + .byte <(MAIN / 1000 .mod 10) + $30 + .byte <(MAIN / 100 .mod 10) + $30 + .byte <(MAIN / 10 .mod 10) + $30 + .byte <(MAIN / 1 .mod 10) + $30 + .byte 0 ; end of line +@end: .word 0 ; end of program + +;********************************************************************* +; Starting entry point for this program +; + .segment "STARTUP" + +MAIN: + LDX $FFFC + LDY $FFFD + STX $0318 + STY $0319 ; enable RESTORE key as RESET + LDA MACHINE + CMP #$05 + BEQ NTSC + CMP #$0C + BEQ PAL +READY: JMP RESET ; not a VIC? + ; + ; NTSC setup +NTSC: LDX #<@NTSC ; load the timer low-byte latches + STX $9126 + LDX #>@NTSC + LDA #$75 ; raster line 234/235 + BNE IRQSYNC +@NTSC = $4243 ; (261 * 65 - 2) + ; + ; PAL setup +PAL: LDX #<@PAL ; load the timer low-byte latches + STX $9126 + LDX #>@PAL + LDA #$82 ; raster line 260/261 + BNE IRQSYNC +@PAL = $5686 ; (312 * 71 - 2) + ; +IRQSYNC: + CMP VIC+$04 + BNE IRQSYNC + STX $9125 ; load T1 latch high, and transfer both bytes to T1 counter + ; init VIC + LDA #$00+$16 ; set for videoram @ $1400 with 22-columns + STA VIC+$02 ; video matrix address + columns + LDA #%10101110 ; 8x8 height + 23-rows + STA VIC+$03 ; rows / character height + LDA #$DF ; set video @ $1400 and char table @ $1C00 + STA VIC+$05 + LDA #$2F ; red aux, highest volume + STA VIC+$0E + LDA #221 ; Programmer's Reference Guide: Appendix B + STA VIC+$0F ; lt green screen / green border + ; reset sound channels +@cont: LDA #$00 + TAY +@snd: STA VIC+$0A,Y + INY + CPY #$04 + BNE @snd + ; setup my background processing + .global MYIRQ + SEI + LDX #MYIRQ + STX $0314 + STY $0315 + CLI + + +;********************************************************************* +; Now that all the VIC startup initialization stuff is completed, +; you can append one-time startup code/data here, i.e., like a splash +; title screen. Then, you must jump to your CODE segment, linked +; outside of VIC's internal RAM address space ... +; +RUNONCE: + LDX #SPLASHCOLOR + STX $FB + STY $FC + LDX #$00 + LDY #$94 + STX $FD + STY $FE + LDX #$02 + LDY #$00 +@fill: LDA ($FB),Y + STA ($FD),Y + INY + BNE @fill + INC $FC + INC $FE + DEX + BNE @fill + .global NMES + LDX #10 + STX NMES +@loop: + LDA $028D + AND #$02 ; got C= key? + BNE @go + LDY #$00 + STY $9113 + LDA #$FF + STA $9122 + LDA $9111 + AND #$20 ; got joystick FIRE ? + BNE @loop +@go: + .global EFFECT + .global EINDEX + LDA #$71 + STA EINDEX + LDA #2 + STA EFFECT + LDX #<$1C08 + LDY #>$1C08 + STX $FB + STY $FC + LDA #0 + STA NMES + STA R1 + STA R2 + LDA #(8*16)+1 + STA R0 +@destroy: + DEC R0 + BEQ @bye + LDA R0 + AND #$07 + BNE @cont + JSR @copy +@cont: CLC + LDX #7 +@rol: ROL $1C08,X + ROL $1C00,X + DEX + BPL @rol + LDX JIFFYL + INX +@wait: CPX JIFFYL + BNE @wait + BEQ @destroy +@bye: + .global RESTART ; useful symbol for MAP and hotkey restarting + JMP RESTART ; the entry point into your program +@copy: + LDA R2 + ASL + TAX + LDA CROM,X + STA $FD + LDA CROM+1,X + STA $FE + LDY #7 +@bits: LDA ($FD),Y + STA ($FB),Y + DEY + BPL @bits + INC R1 + LDA R1 + AND #$03 + BNE @fini + INC R2 +@fini: RTS + +CROM: .word $8330,$8B48,$8AF0,$8100 + + +;********************************************************************* +; Display startup splash screen +; redirect VIC to look here and paint the screen with ensuing data +; + .segment "SPLASH" + +SPLASHDATA: + .byte $DA,$A0,$A0,$DA,$A0,$A0,$DA,$A0,$A0,$DA,$A0,$A0,$DA,$A0,$A0,$DA,$A0,$A0,$DA,$A0,$A0,$DA + .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$94,$88,$89,$93,$A0,$87,$81,$8D,$85,$A0,$92,$85,$91,$95,$89,$92,$85,$93,$A0,$81,$A0 + .byte $A0,$A0,$A0,$8A,$8F,$99,$93,$94,$89,$83,$8B,$A0,$94,$8F,$A0,$90,$8C,$81,$99,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$00,$00,$00,$00,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$A0,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$00,$00,$00,$A0,$A0,$00,$00,$A0,$A0,$00,$00,$00,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$00,$00,$A0,$A0,$00,$00,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$00,$00,$A0,$00,$00,$A0,$00,$00,$A0,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$00,$00,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$00,$00,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $93,$90,$92,$89,$94,$85,$A0,$89,$8E,$96,$81,$84,$85,$92,$93,$A0,$92,$B1,$B0,$AE,$B3,$B0 + .byte $A0,$A0,$7F,$B2,$B0,$B1,$B1,$A0,$92,$8F,$82,$85,$92,$94,$A0,$88,$95,$92,$93,$94,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$8D,$81,$84,$85,$A0,$89,$8E,$A0,$95,$93,$81,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$79,$7C,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $A0,$90,$92,$85,$93,$93,$A0,$7A,$7D,$A0,$94,$8F,$A0,$83,$8F,$8E,$94,$89,$8E,$95,$85,$A0 + .byte $A0,$A0,$A0,$A0,$A0,$A0,$A0,$7B,$7E,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 + .byte $DA,$A0,$A0,$DA,$A0,$A0,$DA,$A0,$A0,$DA,$A0,$A0,$DA,$A0,$A0,$DA,$A0,$A0,$DA,$A0,$A0,$DA + .res 6 +SPLASHCOLOR: + .byte $00,$00,$00,$01,$00,$00,$02,$00,$00,$03,$00,$00,$04,$00,$00,$05,$00,$00,$06,$00,$00,$07 + .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$00 + .byte $00,$00,$00,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$00,$08,$08,$08,$08,$08,$08,$08,$08,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$08,$08,$08,$00,$00,$08,$08,$00,$00,$08,$08,$08,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$00,$00,$08,$08,$00,$00,$08,$08,$00,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$00,$08,$08,$00,$08,$08,$00,$08,$08,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$08,$08,$00,$00,$00,$00,$00,$00,$00,$00,$08,$08,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$03,$03,$03,$03,$03,$03 + .byte $00,$00,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$00,$00 + .byte $00,$00,$00,$00,$00,$03,$03,$03,$03,$03,$03,$03,$00,$02,$01,$06,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$0E,$0E,$0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$0E,$0E,$0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$0E,$0E,$0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + .byte $07,$00,$00,$06,$00,$00,$05,$00,$00,$04,$00,$00,$03,$00,$00,$02,$00,$00,$01,$00,$00,$00 + .res 6 + + +;********************************************************************* +; VIC Software Sprite Stack 2010 (VIC-SSS-MMX) +; +; The above BASIC loader will be overwritten by SSS upon its +; initialization (SSSINIT). The linker will fill this reserved space +; with values used for the dual video frame buffers, play field, and +; the sprite image buffers and registers: 4096 - 6207 ($1000 - $1BFF) +; +; $1000 - $11FF VICFRAME1 - first video buffer +; $1200 - $13FF VICFRAME2 - second video buffer +; $1400 - $15FF PLAYFIELD - write-pending screen buffer +; $1600 - $17FF PLAYCOLOR - write-pending color / dirty buffer +; $1800 - $1BFF Sprite image buffers & registers +; + .segment "SSSBUF" + + ; sprite images: 3 + 4 + 6 + 6 + 3 + 4 + 2 + 4 +SSSBUF: .res 32 * 8 ; if all 64-chars are used by sprites, that + ; exhausts all 128 custom characters for + ; double-buffering (x2) + +; +; SPRITE REGISTERS +; +SPRITEBACK: .res SPRITEMAX ; 1st char this sprite is in collision with +SPRITEBUFH: .res SPRITEMAX ; pointer within sprite image buffer +SPRITEBUFL: .res SPRITEMAX ; pointer within sprite image buffer +SPRITEC1H: .res SPRITEMAX ; pointer within sprite display character pool +SPRITEC1L: .res SPRITEMAX ; pointer within sprite display character pool +SPRITEC2H: .res SPRITEMAX ; pointer within sprite display character pool +SPRITEC2L: .res SPRITEMAX ; pointer within sprite display character pool +SPRITECOL: .res SPRITEMAX ; 4-bit VIC color code +SPRITECX: .res SPRITEMAX ; sprite collision X-coord +SPRITECY: .res SPRITEMAX ; sprite collision Y-coord +SPRITEDEF: .res SPRITEMAX ; function/matrix definition (see explanation below) +SPRITEH: .res SPRITEMAX ; number of raster lines (1-16) +SPRITEIMGH: .res SPRITEMAX ; pointer to source graphic for rendering at 0,0 +SPRITEIMGL: .res SPRITEMAX ; pointer to source graphic for rendering at 0,0 +SPRITEX: .res SPRITEMAX ; horizontal pixel coordinate, visible >0 - 0 - BASESHIP + JSR SSSANIM + LDX #(12*8) + LDY #(23*8) + JSR SSSMOVEXY + JSR INVADERS + LDY #0 ; immediate + JSR SSSFLIP ; gratuitous + ; + LDA #8 + STA R2 +@flash: LDA SPRITEDEF+39 + EOR #$80 ; toggle enable + STA SPRITEDEF+39 + LDY #18 ; ~ 1/3 second pause + JSR SSSFLIP + DEC R2 + BNE @flash + ; + INC STEPS + PLA ;-- + STA NMES + + +;********************************************************************* +; Main game-playing loop +; +GAMELOOP: + JSR FLYBY + JSR INVADERS + JSR NMEFIRE + JSR MOVEBASE + ; + LDY FLIP + JSR SSSFLIP + INC FRAME + ; + JSR BASEFIRE + LDA NMES + BNE GAMELOOP + ; + LDX PLAYER + INC LEVEL,X + ; +NEXTPLAYER: + LDA PLAYERS + CMP #1 + BEQ @fini + LDA PLAYER + EOR #1 + TAX + LDA BASES,X + BEQ @fini ; any bases left? +@np: STX PLAYER ; swap player up +@fini: JMP NEXTLEVEL + + +;********************************************************************* +; End of game sequence +; +GAMEOVER: + LDA #$8F ; orange aux, highest volume + STA VIC+$0E + LDA #0 + STA NMES + STA LEVEL + STA LEVEL+1 + STA PLAYER + STA UFO + LDA MODE + JSR SWITCHMODE + INC PLAYER + JSR SCORESTATUS + INC PLAYER + ; + LDY #0 + STY $C6 ; empty keyboard buffer + DEY + STY R3 + STY FRAME + LDY #4 + STY R4 +@cont: + LDX #6 + LDY #0 + JSR SSSPLOT + LDA JIFFYL + AND #$40 + BNE @text + JSR SSSPRINTS + .byte $C2,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0,$00 + JMP @anim +@text: JSR SSSPRINTS ; print GAME + .byte $C2,$87,$81,$8D,$85,$00 + INC CRSRCOL + INC CRSRCOL + JSR SSSPRINTS ; print OVER + .byte $8F,$96,$85,$92,$00 +@anim: + JSR INVADERS + LDY INVDY + BEQ @vic20 + LDY R3 + BNE @flip + STY INVDY ; aliens should only walk left/right +@vic20: STY R3 +@flip: LDY #0 ; immediate + JSR SSSFFLIP ; skip a frame, if necessary + INC FRAME + LDA FRAME + AND #$1F + BNE @start + INC R4 + LDA R4 + CMP #4 + BCC @fkey + LDA #0 + STA R4 +@fkey: CMP #0 + BNE @fkey3 + LDX #1 + LDY #54 + JSR SHOWSTART + JMP @fbot +@fkey3: CMP #1 + BNE @fkey5 + LDX #2 + LDY #55 + JSR SHOWSTART + JMP @fbot +@fkey5: CMP #2 + BNE @fkey7 + JSR SHOWDIFFICULTY + JMP @fbot +@fkey7: JSR SHOWMODE +@fbot: JSR SHOWBOTTOM +@start: + LDY #$00 + STY $9113 +@joy: LDA #$FF + STA $9122 + LDY $9111 + TYA + AND #$20 ; got joystick FIRE ? + BEQ @p1 +@kb: JSR GETIN + BEQ @next + LDX #200 + STX FRAME + STX VIC+$0C ; 3rd voice + LDX #6 + STX BEEP +@f1: CMP #$85 + BEQ @p1 +@f3: CMP #$86 + BEQ @p2 +@f5: CMP #$87 + BNE @f7 + INC DIFFICULTY + LDA DIFFICULTY + CMP #3 + BCC @xf5 + LDA #0 +@xf5: STA DIFFICULTY + JSR SHOWDIFFICULTY + JMP @cont +@f7: CMP #$88 + BNE @next + LDA MODE + EOR #1 + STA R3 + JSR SWITCHMODE +@next: JMP @cont +@p1: + LDX #1 + STX PLAYERS + LDY #54 + BNE @fini +@p2: + LDA #3 + STA BASES+1 + LDX #2 + STX PLAYERS + LDY #55 +@fini: + JSR SHOWSTART + LDY #0 ; immediate flip for a + JSR SSSFLIP ; gratuitous video update, + LDY #50 ; then show again with a + JSR SSSFLIP ; short pause + LDX #0 + STX EXTRA + STX EXTRA+1 + STX PLAYER + STX SCORE + STX SCORE+1 + STX SCORE+2 + STX SCORE+3 + LDA DIFFICULTY + ASL + STA LEVEL + STA LEVEL+1 + LDA #3 + STA BASES + LDA JIFFYL + AND #$03 ; randomize mothership 1st appearance + STA JIFFYM + INC JIFFYM + RTS + + +;********************************************************************* +; Player missile firing solution +; +BASEFIRE: + LDA BASEHIT + BEQ @missile + LDA #0 + STA BASEHIT + ASL SPRITEDEF+41 + LSR SPRITEDEF+41 + LDA SPRITEZ+41 + AND #%1000 + BEQ @fini ; harmless miss + LDX #41 + JSR DODAMAGE +@fini: RTS +@missile: + LDA SPRITEDEF+41 + ASL + BCS @cont + RTS +@cont: + LDA #0 + STA VIC+$0D ; mute missile launch + LDA SPRITEZ+41 + AND #%1000 ; collision? + BNE @hit + LDA SPRITEY+41 + SEC + SBC #6 + CMP #16 + BCC @top + STA SPRITEY+41 + LDX #41 + JSR SSSTOUCH + RTS + ; explode harmlessly at top of screen +@top: LDA #%10101100 ; enable+ghost+float +@exp: DEC SPRITEX+41 + DEC SPRITEX+41 + LDX #DAMAGE41 +@done: STA SPRITEDEF+41 + STX SPRITEIMGL+41 + STY SPRITEIMGH+41 + LDY #8 ; lengthen to 8-pixels high + STY SPRITEH+41 + LDX #41 + JSR SSSTOUCH + INC BASEHIT + ; re-calculate for stepping speed + LDY #0 + LDX NMES + CPX #27 + BCS @bool + INY + CPX #17 + BCS @bool + INY + CPX #11 + BCS @bool + INY + CPX #6 + BCS @bool + INY + CPX #4 + BCS @bool + INY + CPX #2 + BCS @bool + INY +@bool: STY INVINDEX + LDY #0 ; fastest frame flip + LDA DIFFICULTY + BNE @nmes ; not for normal and harder modes + INY ; keep refresh constant (slower) +@nmes: LDA NMES + CMP #11 + BCS @flip ; < 11 invaders, + INY ; let's not get too frenetic +@flip: STY FLIP + RTS +@erase: + ASL SPRITEDEF+41 + LSR SPRITEDEF+41 + RTS +; +; Sprites custom character allocation (64) +; mothership: 7f,7e,7d,7c,7b,7a +; alien 1: 79,78,77,76,75,74,73,72 +; alien 2: 71,70,6f,6e,6d,6c,6b,6a,69,68,67,66 +; alien 3: 65,64,63,62,61,60,5f,5e,5d,5c,5b,5a +; baseship: 59,58,57,56,55,54 +; laser: 53,52,51,50,4f,4e,4d,4c +; missile: 4b,4a,49,48,47,46,45,44 +; Unallocated (10): +; free chars: 43,42,41,40,3f,3e,3d,3c,3b,3a +; Graphic custom character allocation (18): +; f-keycaps: 39,38,37,36,35,34,33 +; base icon: 32 +; digit font: 31,30,2f,2e,2d,2c,2b,2a,29,28 +; Shields custom character allocation (40): +; shield 4: 27,26,25,24,23,22,21,20,1f,1e +; shield 3: 1d,1c,1b,1a,19,18,17,16,15,14 +; shield 2: 13,12,11,10,0f,0e,0d,0c,0b,0a +; shield 1: 09,08,07,06,05,04,03,02,01,00 +; +@hit: + LDA SPRITEBACK+41 + CMP #$7A + BCC @invader + JMP @mothership +@invader: + CMP #$72 + BCS @alien1 + CMP #$66 + BCS @alien2 + CMP #$5A + BCS @alien3 + LDA #%11101100 ; hit your own shield, + JMP @exp ; do damage to it next frame +@alien1: + LDX #10 +@a1find: + LDA SPRITEDEF,X + ASL + BCC @a1next + LDA SPRITEX+41 + SEC + SBC SPRITEX,X + CMP #8 + BCS @a1next + LDY #$30 + BNE @destroy +@a1next: + DEX + BNE @a1find +@alien2: + LDX #24 +@a2find: + LDA SPRITEDEF,X + ASL + BCC @a2next + LDA SPRITEX+41 + SEC + SBC SPRITEX,X + CMP #10 + BCS @a2next + LDY #$20 + BNE @destroy +@a2next: + DEX + CPX #10 + BNE @a2find +@alien3: + LDX #38 +@a3find: + LDA SPRITEDEF,X + ASL + BCC @a3next + LDA SPRITEX+41 + SEC + SBC SPRITEX,X + CMP #12 + BCS @a3next + LDY #$10 + BNE @destroy +@a3next: + DEX + CPX #24 + BNE @a3find +@destroy: + LDA #1 + STA EFFECT ; invader effect + LDA #17 + STA EINDEX + DEC NMES + ASL SPRITEDEF,X + LSR SPRITEDEF,X + LDA SPRITEX,X + STA SPRITEX+41 + CPY #$30 + BEQ @adj + INC SPRITEX+41 + CPY #$20 + BEQ @adj + INC SPRITEX+41 +@adj: LDA SPRITEY,X + STA SPRITEY+41 + LDA NMEPOS-1,X ; retrieve shot invader from matrix + TAX + LDA #0 + STA NMECOL0-1,X ; disable from future firing solution + JSR SCOREUPDATE + LDA #%10101100 + LDX #EXPLODE + JMP @done +@mothership: + LDA UFO + BNE @ufo + JMP @erase +@ufo: LDA #2 + STA EFFECT ; mothership bonus effect + LDA #$31 + STA EINDEX + LDX #0 + STX UFO + JSR SSSTOUCH + LDY #$50 ; start with 50-points + JSR SCOREUPDATE + LDX #SCORE50 + LDA FRAME + AND #$07 ; oooh, a mystery score? + BNE @100 + LDA #$71 + STA EINDEX + LDA #5 + STA R4 +@250: LDY #$50 ; +250 + JSR SCOREUPDATE + DEC R4 + BNE @250 + LDX #SCORE300 + BNE @50 +@100: AND #$01 + BNE @50 + LDA #$51 + STA EINDEX + LDY #$50 ; +50 + JSR SCOREUPDATE + LDX #SCORE100 +@50: STX SPRITEIMGL + STY SPRITEIMGH + LDA #2 ; red + STA SPRITECOL + JMP @erase + + +;********************************************************************* +; Baseship destruction sequence +; +DEATH: + LDA MODE + BEQ @classic + LDA #170 ; pink screen / red border + STA VIC+$0F +@classic: + LDA #193 + STA VIC+$0D ; noise + LSR + SBC NMES + STA DATASETTE + LDY #0 ; clear this level + STY NMES + STY EFFECT + JSR SSSFLIP ; gratuitous +@loop: + LDX #BASEFIRE1 + LDA DATASETTE + AND #$01 + BEQ @b1 + LDX #BASEFIRE2 +@b1: STX SPRITEIMGL+39 + STY SPRITEIMGH+39 + JSR SSSREFRESH + JSR FLYBY + LDY #2 ; allow the phosphur to burn a little + JSR SSSFLIP + INC FRAME + JSR BASEFIRE + DEC VIC+$0D + DEC VIC+$0D + DEC DATASETTE + BNE @loop + ; + ASL SPRITEDEF+39 + LSR SPRITEDEF+39 + LDA #0 + STA VIC+$0D ; mute noise + JSR ESCAPE + ; + LDX PLAYER + DEC BASES,X + LDA BASES + BNE @nextp + LDA BASES+1 + BNE @nextp + JMP RESTART +@nextp: JMP NEXTPLAYER + + +;********************************************************************* +; Incur some particle damage to a player's shield +; pass X for sprite (40=laser or 41=missile) +; +DODAMAGE: + JSR SSSUSE + LDA SPRITEBUFH,X + STA VECTOR1+1 + STA VECTOR2+1 + LDA SPRITEBUFL,X + STA VECTOR1 + CLC + ADC sssNEXT + BCC @cc1 + INC VECTOR2+1 +@cc1: STA VECTOR2 + ; + LDA SPRITEY,X + TAY +@x40: LDA SPRITEX,X + TAX + JSR SSSPEEKXY + LDY #0 + LSR + BCC @cc2 + LDY #8 +@cc2: STY R1 ; save top of shield offset + ASL + JSR SSSIMAGE + STX VECTORBG + STY VECTORBG+1 + STY VECTORFG+1 + TXA + CLC + ADC #16 + BCC @cc3 + INC VECTORFG+1 +@cc3: STA VECTORFG + ; + LDX sssNUM + LDA SPRITEY,X + AND #$07 + STA R0 + CLC + ADC R1 + STA R1 ; add collision offset to shield + ; + LDA #8 + STA R2 +@bits: LDY R0 + LDA (VECTOR1),Y + EOR #$FF + LDY R1 + AND (VECTORBG),Y + STA (VECTORBG),Y + ; + LDY R0 + LDA (VECTOR2),Y + EOR #$FF + LDY R1 + AND (VECTORFG),Y + STA (VECTORFG),Y + ; + INC R0 + INC R1 + LDA R1 + CMP #16 ; past bottom of shield? + BCS @fini + DEC R2 + BNE @bits +@fini: + JSR SSSREFRESH ; force all sprites to redraw + RTS + + +;********************************************************************* +; Something to distract the player by +; +FLYBY: + LDA SPRITEDEF + ASL + BCS @active + LDA NMES + CMP #10 + BCC @fini + LDA JIFFYM + AND #%111 + BEQ @launch +@fini: RTS +@launch: + INC UFO + LDA SPRITEDEF + ORA #$80 + STA SPRITEDEF ; enable mothership sprite + LDY #16 + STY SPRITEY + LDX #0 + STX MOTHERDX + LDA FRAME + AND #$01 + BEQ @right + INC MOTHERDX + LDX SSSCLIPX +@right: STX SPRITEX + LDA #2 ; red + LDY MODE + BEQ @hull + LDA #9 ; multicolor with white +@hull: STA SPRITECOL +@active: + LDA FRAME + AND #$07 + TAX + LDA MASK,X + AND #%10101010 ; speed + BNE @cont + RTS +@cont: + LDA UFO + BNE @alive + LDA EINDEX + BEQ @dead + RTS +@dead: ASL SPRITEDEF + LSR SPRITEDEF + RTS +@alive: LDX MOTHERDX + BEQ @mover +@movel: DEC SPRITEX + DEC SPRITEX + BNE @anim + BEQ ESCAPE +@mover: INC SPRITEX + INC SPRITEX + LDA SPRITEX + CMP SSSCLIPX + BCS ESCAPE +@anim: + LDX #MOTHERSHIP1 + LDA MODE + BEQ @hires + LDX #MOTHERVIC1 +@hires: LDA SPRITEX + LSR + AND #$01 + BEQ @ms1 + LDX #MOTHERSHIP2 + LDA MODE + BEQ @ms1 + LDX #MOTHERVIC2 +@ms1: STX SPRITEIMGL + STY SPRITEIMGH + LDX #0 + JSR SSSTOUCH + RTS +ESCAPE: LDX #1 ; mothership escapes! penalize the + STX JIFFYM ; player by delaying next appearance + DEX + STX UFO + DEX + STX JIFFYL + ASL SPRITEDEF + LSR SPRITEDEF + RTS + + +;********************************************************************* +; Here they come +; +INVADERS: + LDA INVDY + BEQ @stepping + LDA #0 + STA R0 + LDX #38 + LDA MODE + BEQ @classic + DEC INVDY + DEC INVDY ; since we're moving 2-pixels down +@dmove: + INC SPRITEY,X + INC SPRITEY,X + LDA SPRITEDEF,X + ASL + BCC @dnext + LDA SPRITEY,X + CMP #(23*8) + BNE @dnext + INC R0 +@dnext: DEX + BNE @dmove + LDA R0 + BNE @td ; touchdown! + LDA SPRITEY+1 + AND #$04 + BNE @d2 +@d1: JMP @anim1 +@d2: JMP @anim2 +@classic: + LDA SPRITEY,X + CLC + ADC INVDY + STA SPRITEY,X + LDA SPRITEDEF,X + ASL + BCC @cnext + LDA SPRITEY,X + CMP #(23*8) + BNE @cnext + INC R0 +@cnext: DEX + BNE @classic + STX INVDY ; no more + LDA R0 + BNE @td ; touchdown! + JMP @anim +@td: PLA ; pop return address + PLA + LDA #>(DEATH-1) ; replace with DEATH sequence + PHA + LDA #<(DEATH-1) + PHA + JMP @anim +@stepping: + LDA FRAME + AND #$07 + TAX + LDA MASK,X + LDY INVINDEX + AND SPEED,Y + BNE @cont + RTS +@cont: + LDY INVINDEX + LDA STRIDE,Y + STA ACOPY + LDX #38 + LDA INVDX + BEQ @rmove +@lmove: + LDA SPRITEX,X + SEC + SBC ACOPY + STA SPRITEX,X + LDA SPRITEDEF,X + ASL + BCC @lok + LDA SPRITEX,X + CMP #17 + BCS @lok + LDA #0 + STA INVDX ; go right on next pass + LDA #8 + STA INVDY ; and go down +@lok: DEX + BNE @lmove + BEQ @anim +@rmove: + LDA SPRITEX,X + CLC + ADC ACOPY + STA SPRITEX,X + LDA SPRITEDEF,X + ASL + BCC @rok + LDA SPRITEX,X + CMP #(23*8)-2 + BCC @rok + STA INVDX ; go left on next pass + LDA #8 + STA INVDY ; and go down +@rok: DEX + BNE @rmove +@anim: + LDA SPRITEX+1 + AND #$02 + BNE @anim2 +@anim1: + LDX #INVADER1A + STX SPRITEIMGL+1 + STY SPRITEIMGH+1 + LDX #INVADER2A + STX SPRITEIMGL+11 + STY SPRITEIMGH+11 + LDX #INVADER3A + STX SPRITEIMGL+25 + STY SPRITEIMGH+25 + BNE @fini +@anim2: + LDX #INVADER1B + STX SPRITEIMGL+1 + STY SPRITEIMGH+1 + LDX #INVADER2B + STX SPRITEIMGL+11 + STY SPRITEIMGH+11 + LDX #INVADER3B + STX SPRITEIMGL+25 + STY SPRITEIMGH+25 +@fini: + LDA SPRITEZ+1 + ORA #%11100010 ; force make / copy / null + fast copy + STA SPRITEZ+1 ; on alien1 master sprite + LDA SPRITEZ+11 + ORA #%11100010 ; force make / copy / null + fast copy + STA SPRITEZ+11 ; on alien2 master sprite + LDA SPRITEZ+25 + ORA #%11100010 ; force make / copy / null + fast copy + STA SPRITEZ+25 ; on alien3 master sprite + RTS + + +;********************************************************************* +; Process player input +; +MOVEBASE: + LDY #$00 + STY $9113 + LDA #$7F + STA $9122 + LDA $9120 + AND #$80 + BNE @joy1 + INC SPRITEX+39 ; move right + INC SPRITEX+39 + BNE @check +@joy1: + LDA #$FF + STA $9122 + LDA $9111 + AND #$10 + BNE @fire + DEC SPRITEX+39 ; move left + DEC SPRITEX+39 +@check: + LDX #39 + JSR SSSTOUCH ; redraw base + LDA SPRITEX+39 + CMP #(3*8) + BCS @okl + LDA #(3*8) +@okl: CMP #(21*8) + BCC @okr + LDA #(21*8) +@okr: STA SPRITEX+39 ; update + AND #$07 + BNE @fire + CMP #(12*8)+1 ; influence next firing column + BCC @div2 + ASL NMENEXT + BNE @fire +@div2: LSR NMENEXT +@fire: + LDA SPRITEDEF+41 + ASL + BCS @fini ; missile still active? + LDA $9111 + AND #$20 ; got joystick FIRE ? + BNE @fini + LDA #%11100100 ; enable + collision + ghost + float Y + STA SPRITEDEF+41 + LDA #6 ; shorten to 6-pixels high + STA SPRITEH+41 + LDA #<$8328 + STA SPRITEIMGL+41 + LDA #>$8328 + STA SPRITEIMGH+41 + LDA SPRITEX+39 + CLC + ADC #7 + STA SPRITEX+41 + LDY #(23*8)-6 + STY SPRITEY+41 + LDX #41 + JSR SSSTOUCH ; redraw missile + LDA #$FE + STA VIC+$0D +@fini: RTS + + +;********************************************************************* +; Enemy laser firing solution +; +NMEFIRE: + INC NMENEXT + LDA NMENEXT + CMP #13 + BCC @next + LDA #0 + STA NMENEXT + ; check for existing laser +@next: LDA SPRITEDEF+40 + ASL + BCS @cont + INC NMEBOLT + LDA NMEBOLT + AND #$03 + CMP #$03 + BEQ @fini + JMP @emit +@cont: + LDA SPRITEZ+40 + AND #%1000 ; collision? + BNE @hit +@descend: + LDA SPRITEY+40 + CLC + ADC #3 + STA SPRITEY+40 + CMP #(24*8) ; just a border sanity check, + BCS @miss ; should never occur +@anim: + LDA NMEBOLT + AND #$03 + ASL + TAX + LDA LASER,X + STA SPRITEIMGL+40 + LDA LASER+1,X + STA SPRITEIMGH+40 + LDA FRAME + AND #$03 + ASL + CLC + ADC SPRITEIMGL+40 + BCC @cc + INC SPRITEIMGH+40 +@cc: STA SPRITEIMGL+40 +@anim2: LDX #40 ; redraw laser + JSR SSSTOUCH +@fini: RTS +@miss: + LDA #%10101100 ; floating 8x8 ghost w/o collision + STA SPRITEDEF+40 +@hit: + LDA SPRITEBACK+40 + CMP #$80 + BCS @rom + CMP #$5A ; friendly fire? + BCS @descend ; allow laser to passthru +@rom: LDA NMEHIT + BNE @hit2 + INC NMEHIT ; set flag that bolt has exploded + DEC SPRITEX+40 ; adjust X for explosion image + LDA SPRITECY+40 + CMP SPRITEY+40 + BCC @lower ; don't explode any higher + AND #$F8 + STA SPRITEY+40 ; adjust Y for explosion image +@lower: LDX #DAMAGE40 + STX SPRITEIMGL+40 + STY SPRITEIMGH+40 + BNE @anim2 +@hit2: + LDA SPRITEBACK+40 + CMP #$28 + BCC @shield ; hit part of player's shield? + CMP #$54 ; baseship sprite lo char + BCC @done + CMP #$5A ; baseship sprite hi char + BCS @done +@gotme: PLA ; pop return address + PLA + LDA #>(DEATH-1) ; replace with DEATH sequence + PHA + LDA #<(DEATH-1) + PHA + BNE @done +@shield: + LDX #40 + JSR DODAMAGE +@done: ASL SPRITEDEF+40 + LSR SPRITEDEF+40 + JMP @anim2 +@emit: + LDA NMENEXT + ASL ; make index to fetch word + TAX + LDA NMECOL,X + STA VECTOR1 + LDA NMECOL+1,X + STA VECTOR1+1 + LDY #0 + STY NMEHIT ; clear flag + STY R0 ; clear invader sprite# to fire +@find: LDA (VECTOR1),Y + BEQ @chk + STA R0 ; save lowest invader + INY + BNE @find +@chk: + LDX R0 + BNE @fire + RTS ; no enemies this column, skip +@fire: + LDA NMES + CMP #20 + BCC @fire2 + CPX #11 ; top enemy row does not fire, until 1/2 + BCS @fire2 ; its invasion force is destroyed . . . + RTS +@fire2: LDA SPRITEX,X + ADC OFFSET-1,X + STA SPRITEX+40 + LDA SPRITEY,X + CLC + ADC #8 + STA SPRITEY+40 + LDY #18 + CMP #(21*8) + BNE @ck2 + JSR REMOVELAYER +@ck2: INY + CMP #(22*8) + BNE @ok + JSR REMOVELAYER +@ok: LDA #%11101100 ; floating 8x8 ghost w/ collision + STA SPRITEDEF+40 + JMP @anim + + +;********************************************************************* +; Remove a shield layer +; pass Y (18 or 19) +; +REMOVELAYER: + CPY #18 + BEQ @y18 + LDA LAYER + CMP #2 + BNE @y19 + RTS ; already removed bottom layer +@y19: + LDX #$08 ; index to bottom + BNE @remove +@y18: + LDX #$00 ; index to top + LDA LAYER + CMP #1 + BNE @remove + RTS ; already removed top layer +@remove: + INC LAYER + LDY #$1C + STX VECTOR1 + STY VECTOR1+1 + LDA #20 + STA R0 + ; +@top: LDA #0 + LDY #7 +@zero: STA (VECTOR1),Y + DEY + BPL @zero + LDA VECTOR1 + CLC + ADC #16 + BCC @cc + INC VECTOR1+1 +@cc: STA VECTOR1 + DEC R0 + BNE @top + RTS + + +;********************************************************************* +; Show player's score +; +SCORESTATUS: + LDX #1 + LDA PLAYER + BEQ @p1 + CMP #2 + BCS @fini + LDX #17 +@p1: LDY #22 + JSR SSSPLOT + LDA #2 + STA R0 + LDA #5 ; green + STA COLORCODE + LDA PLAYER + ASL + TAX +@loop: LDA SCORE,X + LSR + LSR + LSR + LSR + CLC + ADC #40 + JSR SSSPRINT + LDA SCORE,X + AND #$0F + CLC + ADC #40 + JSR SSSPRINT + INX + DEC R0 + BNE @loop + ; + LDX #1 + LDA PLAYER + BNE @p2 + LDX #20 +@p2: LDY #22 + JSR SSSPLOT + LDX PLAYER + LDA BASES,X + BEQ @fini + TAX +@bases: DEX + BEQ @xicon + LDA #50 ; baseship icon + JSR SSSPRINT + LDA PLAYER + BNE @bases + DEC CRSRCOL + DEC CRSRCOL + BNE @bases +@xicon: LDA #$E3 + JSR SSSPRINT +@fini: RTS + + +;********************************************************************* +; Update player's score +; send Y with decimal number to add +; +SCOREUPDATE: + LDA PLAYER + ASL + TAX + INX ; 1's + TYA + SED + CLC + ADC SCORE,X + STA SCORE,X + BCC @cc +@cs: DEX ; too bad if 10,000 is breached ... no one + LDA SCORE,X ; likes a smarty-pants! :P + CLC + ADC #$01 + STA SCORE,X + BCS @cs +@cc: CLD + LDX PLAYER + LDA EXTRA,X + BNE @show + TXA + ASL + TAX + LDA SCORE,X + CMP #$15 + BCC @show + LDA #3 ; bonus effect + STA EFFECT + LDA #$81 + STA EINDEX + LDX PLAYER + INC EXTRA,X ; woot! + INC BASES,X +@show: JSR SCORESTATUS + RTS + + +;********************************************************************* +; Display function key top +; +SHOWTOP: + LDX #4 + LDY #13 + JSR SSSPLOT + JSR SSSPRINTS + .byte $C9,$EC,$E2,$E2,$FB,$00 + LDX #4 + LDY #14 + JSR SSSPLOT + RTS + + +;********************************************************************* +; Display [Fy] START Px +; +SHOWSTART: + STY @n + LDY #$C5 ; green + TXA + CMP #1 + BEQ @p1 + LDY #$C3 ; cyan +@p1: CLC + ADC #$B0 + STA @p + STY @c + JSR SHOWTOP + JSR SSSPRINTS + .byte 51,52 +@n: .byte 54,$E1 +@c: .byte $C5,$A0,$93,$94,$81,$92,$94,$A0 +@p: .byte $B1,$90,$00 + JMP SHOWBOTTOM + + +;********************************************************************* +; Display [F5] EASIER|NORMAL|HARDER +; +SHOWDIFFICULTY: + JSR SHOWTOP + JSR SSSPRINTS + .byte 51,52,56,$E1,$00 + LDA DIFFICULTY + BNE @nez +@ez: JSR SSSPRINTS + .byte $C7,$A0,$85,$81,$93,$89,$85,$92,$A0,$A0,$00 + RTS +@nez: CMP #1 + BNE @adv + JSR SSSPRINTS + .byte $C5,$A0,$8E,$8F,$92,$8D,$81,$8C,$A0,$A0,$00 + RTS +@adv: JSR SSSPRINTS + .byte $C4,$A0,$88,$81,$92,$84,$85,$92,$A0,$A0,$00 + RTS + + +;********************************************************************* +; Display [F7] CLASSIC| VIC= 20 +; +SHOWMODE: + JSR SHOWTOP + JSR SSSPRINTS + .byte 51,52,57,$E1,$00 + LDA MODE + BNE @vic20 +@classic: + JSR SSSPRINTS + .byte $C1,$A0,$83,$8C,$81,$93,$93,$89,$83,$A0,$00 + RTS +@vic20: + JSR SSSPRINTS + .byte $C6,$A0,$96,$C7,$89,$C5,$83,$C2,$BD,$A0,$C1,$B2,$B0,$A0,$00 + RTS + + +;********************************************************************* +; Display function key bottom +; +SHOWBOTTOM: + LDX #4 + LDY #15 + JSR SSSPLOT + JSR SSSPRINTS + .byte $C9,$FC,53,53,$FE,$00 + LDY #0 ; immediate + JSR SSSFLIP + RTS + + +;********************************************************************* +; Draw bottom status line +; +STATUSLINE: + LDA #5 ; green + STA COLORCODE + LDX #0 + LDY #23 + JSR SSSPLOT +@draw: LDA #$E3 + JSR SSSPRINT + LDX CRSRCOL + BNE @draw + RTS + + +;********************************************************************* +; Pass A for MODE desired (0=CLASSIC, 1=DELUXE) +; +SWITCHMODE: + STA MODE + BNE @deluxe +@classic: + LDA #8 ; black screen / black border + BNE @sprites +@deluxe: + LDA #12 ; black screen / magenta border + LDY PLAYER + BEQ @sprites + LDA #14 ; black screen / blue border + ; +@sprites: + STA VIC+$0F + LDX #1 ; fill with white character color + STX COLORCODE + JSR SSSINIT ; initialize software sprite stack +@init: + ; mothership + LDA #%00001010 ; float horizontal 16w x 8h sprite + LDY #8 + JSR SSSCREATE + ; + LDY #(4*8) ; start from top + LDA MODE + BEQ @dy + LDY #(-7*8)-2 ; start from off-screen +@dy: STY R0 + ; + ; top row invaders + LDX MODE + LDA ALIENS,X ; floating 8x8 sprite + LDY #8 + JSR SSSCREATE + LDA #7 ; yellow + LDX #INVADER1A + JSR SSSANIM + LDX #(3*8)+4 + LDY R0 + JSR SSSMOVEXY +@alien1: + LDX MODE + LDA ALIENS,X + ORA #SPRITEDEF4 ; repeating sprite + LDY #8 + JSR SSSCREATE + LDA #7 ; yellow + STA SPRITECOL,X + LDA SPRITEX-1,X + CLC + ADC #16 + STA SPRITEX,X + LDA R0 + STA SPRITEY,X + CPX #10 + BNE @alien1 + ; + ; next 2 row of invaders + LDX MODE + LDA ALIENS+2,X ; floating 16w x 8h sprite + LDY #8 + JSR SSSCREATE + LDA #3 ; cyan + LDX #INVADER2A + JSR SSSANIM + LDX #(3*8)+3 + LDA R0 + CLC + ADC #16 + STA R0 + TAY + JSR SSSMOVEXY +@alien2a: + LDX MODE + LDA ALIENS+2,X + ORA #SPRITEDEF4 ; repeating sprite + LDY #8 + JSR SSSCREATE + LDA #3 ; cyan + STA SPRITECOL,X + LDA SPRITEX-1,X + CLC + ADC #24 + STA SPRITEX,X + LDA R0 + STA SPRITEY,X + CPX #17 + BNE @alien2a + LDX #(3*8)+3 + STX XCOPY + LDA R0 + CLC + ADC #16 + STA R0 +@alien2b: + LDX MODE + LDA ALIENS+2,X + ORA #SPRITEDEF4 ; repeating sprite + LDY #8 + JSR SSSCREATE + LDA #3 ; cyan + STA SPRITECOL,X + LDA XCOPY + STA SPRITEX,X + CLC + ADC #24 + STA XCOPY + LDA R0 + STA SPRITEY,X + CPX #24 + BNE @alien2b + ; + ; next 2 row of invaders + LDX MODE + LDA ALIENS+4,X ; floating 16w x 8h sprite + LDY #8 + JSR SSSCREATE + LDA #5 ; green + LDX #INVADER3A + JSR SSSANIM + LDX #(3*8)+2 + STX XCOPY + LDA R0 + CLC + ADC #16 + STA R0 + TAY + JSR SSSMOVEXY +@alien3a: + LDX MODE + LDA ALIENS+4,X + ORA #SPRITEDEF4 ; repeating sprite + LDY #8 + JSR SSSCREATE + LDA #5 ; green + STA SPRITECOL,X + LDA SPRITEX-1,X + CLC + ADC #24 + STA SPRITEX,X + LDA R0 + STA SPRITEY,X + CPX #31 + BNE @alien3a + LDX #(3*8)+2 + STX XCOPY + LDA R0 + CLC + ADC #16 + STA R0 +@alien3b: + LDX MODE + LDA ALIENS+4,X + ORA #SPRITEDEF4 ; repeating sprite + LDY #8 + JSR SSSCREATE + LDA #5 ; green + STA SPRITECOL,X + LDA XCOPY + STA SPRITEX,X + CLC + ADC #24 + STA XCOPY + LDA R0 + STA SPRITEY,X + CPX #38 + BNE @alien3b +@player: + LDA #%00001010 ; float horizontal 16w x 8h sprite + LDY #8 + JSR SSSCREATE +@laser: + LDA #%01101100 ; floating 8x8 ghost w/ collision + LDY #8 + JSR SSSCREATE +@missile: + LDA #%01101100 ; floating 8x8 ghost w/ collision + LDY #8 + JSR SSSCREATE + ; + ; adjust for level + LDA #0 + STA INVINDEX + LDX PLAYER + LDA LEVEL,X + CMP #6 + BCC @cc + LDA #6 +@cc: ASL + ASL + ASL + LDY MODE + BEQ @fixed + CLC + ADC #11*8+2 +@fixed: STA INVDY + ; + ; refresh player's shields + LDX #$00 + LDY #$1C + STX VECTOR1 + STY VECTOR1+1 + LDX #SHIELD + STX VECTOR2 + STY VECTOR2+1 + LDY #4 + STY R2 +@r0: LDY #80 + STY R0 + LDY #79 + STY R1 +@r1: LDA (VECTOR2),Y + DEC R0 + LDY R0 + STA (VECTOR1),Y + DEC R1 + LDY R1 + BPL @r1 + LDA VECTOR1 + CLC + ADC #80 + BCC @np + INC VECTOR1+1 +@np: STA VECTOR1 + DEC R2 + BNE @r0 + ; + LDA VIC+$0F + AND #$07 ; use border color as + STA R4 ; shield paint + LDA MODE + BNE @shields + LDA #5 ; green + STA COLORCODE + LDA SPRITEDEF+39 + ORA #SPRITEDEF5 ; ghost mode on + STA SPRITEDEF+39 + LDX #0 + LDY #18 + JSR SSSPLOT +@cgrn: LDA #SSSNULL ; apply colored cellophane on monitor + JSR SSSPRINT + LDY CRSRROW + BNE @cgrn + LDA #2 ; red + STA COLORCODE +@cred: LDA #SSSNULL ; apply colored cellophane on monitor + JSR SSSPRINT + LDX CRSRCOL + BNE @cred + LDA #5 ; use green as + STA R4 ; shield paint +@shields: + LDA #0 + STA R0 + LDX #1 +@p0: LDA #4 + STA R1 +@p1: LDY #18 +@p2: STX XCOPY + STY YCOPY + JSR SSSPLOT + LDA R4 ; dip brush in shield paint + LDX MODE + BEQ @char ; classic keeps green across entire row + LDX CRSRCOL ; while VIC allows white between shields + CPX #2 + BCC @vic + CPX #5 + BCC @char + CPX #7 + BCC @vic + CPX #10 + BCC @char + CPX #12 + BCC @vic + CPX #15 + BCC @char + CPX #17 + BCC @vic + CPX #20 + BCC @char +@vic: LDA #1 ; make spaces between shields white +@char: STA COLORCODE + LDA R0 + JSR SSSPOKE +@skip: LDY YCOPY + LDX XCOPY + INC R0 + INY + CPY #20 + BNE @p2 + INX + DEC R1 + BPL @p1 + CPX #20 + BCC @p0 + ; + JSR STATUSLINE + JSR SCORESTATUS + RTS + + +;********************************************************************* +; Background software interrupt routine +; +MYIRQ: + CLD + LDA NMES + BEQ @v1 +@step: LDX BEEP + BNE @v1x + ASL + STA BEEP + INC STEPS + LDA STEPS + AND #$07 + TAX + LDA STEP,X + BEQ @v1 + LDX #4 + STX BEEP +@v1: STA VIC+$0A ; voice #1 +@v1x: DEC BEEP + ; + LDA EFFECT + BEQ @v2 + CMP #1 + BNE @ms +@inv: DEC EINDEX + LDA EINDEX + BEQ @xv2 + AND #$07 + BEQ @invd + LDA VIC+$0B + CLC + ADC #4 + BNE @v2 +@invd: LDA #200 + BNE @v2 +@ms: CMP #2 + BNE @bonus + DEC EINDEX + LDA EINDEX + BEQ @xv2 + AND #$08 + BNE @msdec + LDA VIC+$0B + CLC + ADC #$02 + BNE @v2 +@msdec: LDA VIC+$0B + SEC + SBC #$03 + BNE @v2 +@bonus: DEC EINDEX + LDA EINDEX + BEQ @xv2 + AND #$18 + BEQ @v2 + LDA JIFFYL + AND #$01 + BEQ @v2 + LDA #$F0 + BNE @v2 +@xv2: STA EFFECT +@v2: STA VIC+$0B ; voice #2 +@v2x: LDA UFO + BEQ @v3 + INC CYCLE + LDA CYCLE + LSR + LSR + LSR + TAX + CPX #28 + BCC @cycle + LDX #0 ; reset + STX CYCLE +@cycle: LDA MCOLOR,X + STA VIC+$0E ; set new aux color / volume + LDA VIC+$0C + BEQ @v3n + SEC + SBC #3 + CMP #232 + BCS @v3 +@v3n: LDA #247 +@v3: STA VIC+$0C ; voice #3 + JMP SSSIRQ ; process synchronous flipping + + +;********************************************************************* +; Game runtime variables +; +BASEHIT: .byte 0 ; flag when a missile hits +BASES: .res 2 ; players' lives +BEEP: .byte 0 ; duration +CYCLE: .byte 0 ; auxiliary color cycle +DIFFICULTY: .byte 1 ; 0=easier, 1=normal, 2=harder +EFFECT: .byte 0 ; 0=none, 1=invader, 2=mothership, 3=bonus +EINDEX: .byte 0 ; sound effect register +INVDX: .byte 0 ; 0 (right) or <>0 (left) +INVDY: .byte 0 ; 0 (horizontal) <>0 (vertical) +INVINDEX: .byte 0 ; index into SPEED & STRIDE table (0-6) +EXTRA: .res 2 ; extra base bonus @ 1500-points +FLIP: .byte 0 ; wait for vsync counter +FRAME: .byte 0 ; video frame counter for action/animation timing +LAYER: .byte 0 ; remove next shield layer (0-2) +LEVEL: .res 2 ; players' level +MODE: .byte 0 ; 0=classic, 1=color +MOTHERDX: .byte 0 ; 0 (right) or <>0 (left) +NMEBOLT: .byte 0 ; type: 0-2 +NMECOL0: .res 6 ; array with occupied invaders +NMECOL1: .res 2 ; array with occupied invaders +NMECOL2: .res 5 ; array with occupied invaders +NMECOL3: .res 2 ; array with occupied invaders +NMECOL4: .res 6 ; array with occupied invaders +NMECOL5: .res 2 ; array with occupied invaders +NMECOL6: .res 5 ; array with occupied invaders +NMECOL7: .res 2 ; array with occupied invaders +NMECOL8: .res 6 ; array with occupied invaders +NMECOL9: .res 2 ; array with occupied invaders +NMECOLA: .res 5 ; array with occupied invaders +NMECOLB: .res 2 ; array with occupied invaders +NMECOLC: .res 6 ; array with occupied invaders +NMEHIT: .byte 0 ; flag when a bolt hits +NMENEXT: .byte 0 ; next firing column: 0-C +NMES: .byte 0 ; 0-38 +PLAYER: .byte 0 ; player up: 0/1 +PLAYERS: .byte 0 ; # of players up (0 = initial startup) +SCORE: .res 4 ; players' score in BCD +STEPS: .byte 0 ; invader step counter +UFO: .byte 0 ; mothership sound effect + + + .segment "RODATA" + +;********************************************************************* +; Game data +; +ALIENS: .byte %10101100,%10001100,%10101110,%10001110,%10101110,%10001110 +LASER: .word LASER1,LASER2,LASER3 + +MCOLOR: .byte $2F,$AF,$2F,$AF + .byte $8F,$9F,$8F,$9F + .byte $7F,$FF,$7F,$FF + .byte $5F,$DF,$5F,$DF + .byte $3F,$BF,$3F,$BF + .byte $6F,$ED,$6F,$ED + .byte $4F,$CF,$4F,$CF + +OFFSET: .byte 2,2,3,3,2,3,2,2,3,3 + .byte 3,4,3,4,3,4,3 + .byte 4,3,4,3,4,3,4 + .byte 4,5,4,5,4,5,4 + .byte 5,4,5,4,5,4,5 + +NMECOL: .word NMECOL0,NMECOL1,NMECOL2,NMECOL3,NMECOL4 + .word NMECOL5,NMECOL6,NMECOL7,NMECOL8,NMECOL9 + .word NMECOLA,NMECOLB,NMECOLC + +NMEPOS: .byte 1,7,14,16,22,29,31,37,44,46 + .byte 2,9,17,24,32,39,47 + .byte 3,10,18,25,33,40,48 + .byte 4,11,19,26,34,41,49 + .byte 5,12,20,27,35,42,50 + +SPEED: .byte %10000000 ; 27-38 + .byte %10001000 ; 17-26 + .byte %10010010 ; 11-16 + .byte %10101010 ; 6-10 + .byte %11101110 ; 4-5 + .byte %11101110 ; 2-3 + .byte %11111111 ; 1 + +STRIDE: .byte 1,1,1,1,1,2,2 + +STEP: .byte 0,175,0,159,0,147,0,135 + + +;********************************************************************* +; Sprite data +; +BASESHIP: + .byte %00000001 + .byte %00000011 + .byte %00000011 + .byte %01111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + ; + .byte %00000000 + .byte %10000000 + .byte %10000000 + .byte %11111100 + .byte %11111110 + .byte %11111110 + .byte %11111110 + .byte %11111110 +BASEFIRE1: + .byte %00000000 + .byte %00000110 + .byte %00101000 + .byte %01101101 + .byte %10101010 + .byte %11111111 + .byte %11111111 + .byte %11111111 + ; + .byte %00000000 + .byte %00011000 + .byte %10100000 + .byte %10110100 + .byte %10101010 + .byte %11111110 + .byte %11111110 + .byte %11111110 +BASEFIRE2: + .byte %00000000 + .byte %01100000 + .byte %00010010 + .byte %10110110 + .byte %01010101 + .byte %11111111 + .byte %11111111 + .byte %11111111 + ; + .byte %00000000 + .byte %01100000 + .byte %00010100 + .byte %11011000 + .byte %01010100 + .byte %11111110 + .byte %11111110 + .byte %11111110 + +DAMAGE40: + .byte %00110000 + .byte %10011000 + .byte %00110100 + .byte %01111000 + .byte %10111000 + .byte %01111100 + .byte %10111000 + .byte %01010100 + +DAMAGE41: + .byte %01101000 + .byte %10111000 + .byte %01110000 + .byte %11101000 + .byte %01110000 + .byte %01111000 + .byte %11110000 + .byte %01111000 + +INVADER1A: + .byte %00011000 + .byte %00111100 + .byte %01111110 + .byte %11011011 + .byte %11111111 + .byte %00100100 + .byte %01011010 + .byte %10100101 +INVADER1B: + .byte %00011000 + .byte %00111100 + .byte %01111110 + .byte %11011011 + .byte %11111111 + .byte %01011010 + .byte %10000001 + .byte %01000010 + +INVADER2A: + .byte %00100001 + .byte %10010010 + .byte %10111111 + .byte %11101101 + .byte %01111111 + .byte %00111111 + .byte %00100001 + .byte %01000000 + ; + .byte %00000000 + .byte %01000000 + .byte %01000000 + .byte %11000000 + .byte %10000000 + .byte %00000000 + .byte %00000000 + .byte %10000000 +INVADER2B: + .byte %00100001 + .byte %00010010 + .byte %00111111 + .byte %01101101 + .byte %11111111 + .byte %10111111 + .byte %10100001 + .byte %00010010 + ; + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %10000000 + .byte %11000000 + .byte %01000000 + .byte %01000000 + .byte %00000000 + +INVADER3A: + .byte %00001111 + .byte %01111111 + .byte %11111111 + .byte %11100110 + .byte %11111111 + .byte %00011001 + .byte %00110110 + .byte %11000000 + ; + .byte %00000000 + .byte %11100000 + .byte %11110000 + .byte %01110000 + .byte %11110000 + .byte %10000000 + .byte %11000000 + .byte %00110000 +INVADER3B: + .byte %00001111 + .byte %01111111 + .byte %11111111 + .byte %11100110 + .byte %11111111 + .byte %00111001 + .byte %01100110 + .byte %00110000 + ; + .byte %00000000 + .byte %11100000 + .byte %11110000 + .byte %01110000 + .byte %11110000 + .byte %11000000 + .byte %01100000 + .byte %11000000 + +LASER1: ; 0 + .byte %01000000 + .byte %00100000 + .byte %01000000 + .byte %10000000 + .byte %01000000 + .byte %00100000 + .byte %01000000 + .byte %01000000 + ; 1 + .byte %10000000 + .byte %01000000 + ; 2 + .byte %00100000 + .byte %01000000 + ; 3 + .byte %10000000 + .byte %01000000 + +LASER2: ; 0 + .byte %01000000 + .byte %01000000 + .byte %01000000 + .byte %10100000 + .byte %01000000 + .byte %01000000 + .byte %01000000 + .byte %10100000 + ; 1 + .byte %01000000 + .byte %01000000 + ; 2 + .byte %01000000 + .byte %10100000 + ; 3 + .byte %01000000 + .byte %01000000 + +LASER3: ; 0 + .byte %01000000 + .byte %01000000 + .byte %01000000 + .byte %01000000 + .byte %01000000 + .byte %01000000 + .byte %01000000 + .byte %11100000 + ; 1 + .byte %01000000 + .byte %01000000 + ; 2 + .byte %01000000 + .byte %01000000 + ; 3 + .byte %01000000 + .byte %01000000 + +MOTHERSHIP1: + .byte %00000011 + .byte %00001111 + .byte %00011111 + .byte %00111111 + .byte %01001100 + .byte %11111111 + .byte %00111001 + .byte %00010000 + ; + .byte %11000000 + .byte %11110000 + .byte %11111000 + .byte %11111100 + .byte %11001100 + .byte %11111111 + .byte %10011100 + .byte %00001000 +MOTHERSHIP2: + .byte %00000011 + .byte %00001111 + .byte %00011111 + .byte %00111111 + .byte %00110011 + .byte %11111111 + .byte %00111001 + .byte %00010000 + ; + .byte %11000000 + .byte %11110000 + .byte %11111000 + .byte %11111100 + .byte %00110010 + .byte %11111111 + .byte %10011100 + .byte %00001000 + +MOTHERVIC1: + .byte %00000011 + .byte %00001111 + .byte %00111111 + .byte %11111111 + .byte %01100110 + .byte %11111111 + .byte %00111111 + .byte %00001100 + ; + .byte %11000000 + .byte %11110000 + .byte %11111100 + .byte %11111111 + .byte %01100110 + .byte %11111111 + .byte %11111100 + .byte %00110000 +MOTHERVIC2: + .byte %00000011 + .byte %00001111 + .byte %00111111 + .byte %11111111 + .byte %10011001 + .byte %11111111 + .byte %00111111 + .byte %00001100 + ; + .byte %11000000 + .byte %11110000 + .byte %11111100 + .byte %11111111 + .byte %10011001 + .byte %11111111 + .byte %11111100 + .byte %00110000 + +SHIELD: + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + ; + .byte %00000001 + .byte %00011111 + .byte %00111111 + .byte %01111111 + .byte %01111111 + .byte %01111111 + .byte %01111111 + .byte %01111111 + .byte %01111111 + .byte %01111111 + .byte %01111111 + .byte %01111111 + .byte %01111110 + .byte %01111100 + .byte %01111000 + .byte %01111000 + ; + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + ; + .byte %00000000 + .byte %11110000 + .byte %11111000 + .byte %11111100 + .byte %11111100 + .byte %11111100 + .byte %11111100 + .byte %11111100 + .byte %11111100 + .byte %11111100 + .byte %11111100 + .byte %11111100 + .byte %11111100 + .byte %01111100 + .byte %00111100 + .byte %00111100 + ; + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + +EXPLODE: + .byte %00000000 + .byte %01001001 + .byte %00101010 + .byte %00000000 + .byte %01100011 + .byte %00000000 + .byte %00101010 + .byte %01001001 + +SCORE50: + .byte %00000000 + .byte %00111110 + .byte %00100000 + .byte %00111100 + .byte %00000010 + .byte %00100010 + .byte %00011100 + .byte %00000000 + ; + .byte %00000000 + .byte %00111000 + .byte %01000100 + .byte %01000100 + .byte %01000100 + .byte %01000100 + .byte %00111000 + .byte %00000000 +SCORE100: + .byte %00000000 + .byte %00100011 + .byte %01100100 + .byte %00100100 + .byte %00100100 + .byte %00100100 + .byte %01110011 + .byte %00000000 + ; + .byte %00000000 + .byte %00011000 + .byte %10100100 + .byte %10100100 + .byte %10100100 + .byte %10100100 + .byte %00011000 + .byte %00000000 +SCORE300: + .byte %00000000 + .byte %01110001 + .byte %00001010 + .byte %00110010 + .byte %00001010 + .byte %00001010 + .byte %01110001 + .byte %00000000 + ; + .byte %00000000 + .byte %10001100 + .byte %01010010 + .byte %01010010 + .byte %01010010 + .byte %01010010 + .byte %10001100 + .byte %00000000 +