; ECE 476 Microcontrollers ; FINAL PROJECT ; 4/30/2003 ; John Martin Lee and Raymond Chen ; Pac-Man ; ; ; ; RANDOM SCRAWLINGS OF A CRAZED MIND ; ---------------------------------- ; ; There is a large penalty for drawing vertical lines since ; the raster is stored in row-major fashion. One solution may be ; to store the raster in half row-major and half column-major format ; so that the penalties may even out. Another solution would be to ; not do any vertical (or horizontal) updates at all and use idle ; time for character updates. ; ; The goal is Pac-Man, there should be very little if any line ; updates, just draw the map once in the beginning and be done. ; ; ; v0.3 ; - finished pixel pacman move & drawing code ; - pacman moves only when directed, need to fix this ; - pacman should take ~3.31 seconds to horizontally cross 198 pixels ; and ~3.23 seconds to vertically cross 194 pixels. ; pacman actually takes ~3.25 seconds to vertically cross: OK ; pacman actually takes ~2.90 seconds to horizontally cross: BAD!!!!! ; NOTE: Commodore 64 pacman crosses horizontally in 6 seconds flat ; ; v0.31 ; - pacman now moves automatically until stopped by a wall ; still have problem that user can stop pacman by forcing ; a direction change into a wall but not changing position ; solution: disallow direction changes into walls ; - pacman has been slowed to half speed ; horizontal cross in ~5.75 seconds ; vertical cross in ~6.55 seconds ; ; v0.32 ; - pacman cannot be stopped by turning into a wall ; but does not move automatically now, so useless ; ; v0.321 ; - pacman now moves automatically until stopped by a wall ; and cannot be stopped by turning into a wall ; - need to create inner walls now and make pacman obey those, too ; - extend the horizontal and vertical line drawing routines to ; take in start and stop points ; - make another screen array containing an empty room? ; would make it easy to look up where walls are ; ; v0.322 ; - pacman respects all walls now ; - need to program blob pacman and make it still respect walls ; ; v0.33 ; - trying to implement blob pacman and have it respect walls ; just like in the commodore 64 (sometimes touch walls) ; ; v0.34 ; - pacman giving extreme headaches ; - between erasing pacman and drawing him, his byte focus is magically altered ; - solution: store X whenever we stop playing with him ; ; v0.351 ; - pacman playing nice now, sort of ; - pacman obeys walls but makes screen flicker when goign left and right ; - probably shoudl condense erase and draw code ; - probably should just do single point move and check ; - still screwing up when drawing up and down, but walls are obeyed ; ; v0.36 ; - back to easy wall check ; - try to combine erase and draw code ; - reallocated precious LDI enabled registers ; - i can't multiply 22 * 8 ; - the reason why it goes across the screen so fast is because there ; are only 176 pixels, not 198 (which isn't even divisible by 8) ; - still have to get rid of damn flicker and weird loading of bitmap ; - very strange that sometimes lines do not draw unless ; values loaded to temp variable first (rightmost vertical line) ; ; v0.4 ; - major code overhaul ; - doing cycle count of pacman drawing code, will need to split ; - allow only ~400 cycles per routine (@ 8Mhz) ; - flicker is gone, but lines are consumed! ; need 5 per sprite, => 25 lines ; ; v0.41 ; - implementing different wall array ; - no more crap! seems to be some glitch, let it rest for a couple hours, ; magically good again or maybe because i did a check erasure and verify write ; - implemented chomping ; - pacman leaves bits around and chomping sucks ; pacman just looks like ball when hitting a wall ; ; v0.42 ; - try to fix floating bits ; - forget mod3, just use a mod3 counter, so much easier ; - chomping looks MUCH better, still have the bits problem ; - the every 4 frames idea paid off nicely, chomping looks beautiful ; - also, the bits are gone magically, no idea if they'll return ; - want to eventually complete the map and work on dots ; idea: whenever drawing pacman, check dots array and draw dots with pacman ; need to do this with erase, too, crap ; this will blow up the drawing time by a lot, may need to do more ; splitting ; eliminate dots iff pac_x, pac_y = dot_x, dot_y ; - pacman keeps mouth open when boundary hit ; ; v0.43 ; - dots are allowed to survive if pacman passes them right now ; - gotta figure out a way to make position marker disappear ; but draw the visual dot fine, probably check if or'ing in the first byte ; ; v0.44 ; - redoing the pacman position code, will make pacman's center the relevant ; position ; - will need to redo walls ; - need to redo erase and draw ; - should make dot detection and erasure easier ; - will introduce tiny error in dot collision depending on direction of contact ; ; v0.45 ; - completing the map ; - filling in dots ; - bitmap with coordinates is now in coord.bmp, very accurate ; - continuing dot collision code ; - storing pillsleft to SPDR and UDR doesn't seem to work ; they might be too special purpose to be used in general ; found out they can't be used since writing to them starts a transmission ; reading from them reads from the input buffer ; ; v0.5 ; - map complete! (sorta) ; - pills complete! ; - pill counter complete!, try an upcounter to do score ; ; high priority: ; - ghosts! ; first do stationary ghost ; die when you touch it: pause, disappear ghost, then animate pacman death ; ; medium priority: ; - animate ghosts ; - implement ghost logic ; ; low priority: ; - implement megapills, do it by exact location check (only 4) ; also do a countdown in time when consumed to indicate krazy time ; - sounds ; - boot screen, reset level (next level) ; - text score, ready, and boot screen ; ; v0.51 ; - corrected variable name frame to field since we are ; actually drawing fields every line ; - stationary ghost drawn ; - start button implemented ; - added some flags ; - need to do reallocation of regs and I/O regs ; ; v0.53 (4/25/2003) ; - generalized erase and draw code to one chunk that processes ; one half of a bitmap at a time ; - should also make a general box drawing function that ; automagically creates a buffer box in the walls array ; since box code takes up a billion lines ; ; v0.54 (4/26/2003) ; - pacman dies, dissolves, and reappears properly ; - gotta get blinky moving ; ; v0.57 (4/30/2003) ; - missed a bunch of updates ; - two ghosts moving now, one at full speed, other at half ; both with semi-disabled search-and-destroy AI .NOLIST .INCLUDE "8515def.inc" .LIST ; 63.5us @ 8MHz .EQU lineTime = 508 .EQU ScreenTop = 25 ; Assuming stuck with 8Mhz limit ; => try to optimize (22*8) x 200 = 176 x 200 display ; Width is in bytes .EQU Height = 200 .EQU Width = 22 .EQU rightside = 141 .EQU bottomside = 165 .EQU scrbytes = Height * Width ; screen id's .EQU screen_array = 0 .EQU walls_array = 1 .EQU pills_array = 2 ; draw/erase id's .EQU draw_pacman = 0 .EQU draw_ghost = 1 ; death acts .EQU end_actI = 19 .EQU end_actII = 35 .EQU end_actIII = 60 ; FREE FOR GENERAL USE (I/O REGS, FASTER THAN STACK) ; Total of 12 slower than gen regs, faster than inram ; PORTA ignored since Port A used for xram ??? ; PORTC ignored since Port C used for xram ??? ; be careful, although these ports useless, may still be affected by mem access .EQU sync = PORTA ; sync flag .EQU pacman_direction= PORTC ; direction pacman is moving in ; ; DDRA ignored since Port A used for xram ; DDRC ignored since Port C used for xram .EQU lncntlo = DDRA .EQU lncnthi = DDRC ; ; TCNT0 as long as Timer0 is not counting (TCCR0 = 0), free for use .EQU field = TCNT0 ; ; OCR1BH free as long as PWM and interrupting on B-match disabled ; OCR1BL free as long as PWM and interrupting on B-match disabled .EQU pacman_x = OCR1BH .EQU pacman_y = OCR1BL ; ; EEARL free as long as EECR = 0 (never write or read from EEPROM) .EQU mod3 = EEARL ; ; EEDR free as long as EECR = 0 (never write or read from EEPROM) .EQU cntr3 = EEDR ; ; UBRR only if UCR = 0 .EQU ghostcntr = UBRR ; register aliases ; lo regs .DEF temp0 = r0 ; this is what LPM loads to all the time .DEF temp_x = r1 .DEF temp_y = r2 .DEF temp_dir = r3 .DEF diff = r4 .DEF byteoff = r5 .DEF tempsync = r6 .DEF mask1 = r10 .DEF mask2 = r11 .DEF mask3 = r12 .DEF offsetlo = r13 .DEF offsethi = r14 .DEF save = r15 ; hi regs (usable by immediate ops) .DEF temp = r16 .DEF templlo = r17 .DEF templhi = r18 .DEF cntr = r19 .DEF cntr2 = r20 ; needed only during map drawing in the beginning, can be reallocated afterwards .DEF which = r21 ; also killpilldir .DEF horzPT = r22 ; also used locally for mod3 and pill killing .DEF vertPT = r23 ; also used locally for mod3 and pill killing .DEF start = r24 ; also drawwho! .DEF end = r25 ; also blocked .DEF killpilldir = r21 .DEF drawwho = r24 .DEF cantagain = r24 .DEF blocked = r25 ; blast a reg to screen ; Send the video out to PORTB.0 ; Note: video is stored big-endian bytes and little-endian bits ; This is because the STK200 limits output current on PORTB.5,6,7 to 0.5mA ; @0 is the register to be blasted, little-endian-wise .MACRO BLAST OUT PORTB, @0 LSR @0 OUT PORTB, @0 LSR @0 OUT PORTB, @0 LSR @0 OUT PORTB, @0 LSR @0 OUT PORTB, @0 LSR @0 OUT PORTB, @0 LSR @0 OUT PORTB, @0 LSR @0 OUT PORTB, @0 LSR @0 ; zeros reg, can use for final zero .ENDMACRO .DSEG ;################################################################################ ; counters pillsleft: .BYTE 2 ;################################################################################ ; state flags startgame: .BYTE 1 ; death counter ; 0 = alive and well ; 1 - 19 = shock ; 20 - 35 = dissolving ; 36 - 60 = gone deathcounter: .BYTE 1 ;################################################################################ ; ghost info stuff ghost1_x: .BYTE 1 ghost1_y: .BYTE 1 ghost1_direction: .BYTE 1 ghost2_x: .BYTE 1 ghost2_y: .BYTE 1 ghost2_direction: .BYTE 1 ;################################################################################ ; screen arrays .ORG 0x260 ; start of external ram screen: .BYTE scrbytes ; char screen[scrbytes] ; contains TV picture walls: .BYTE scrbytes ; char walls[srybtyes] ; contains walls pills: .BYTE scrbytes ; char pills[srybtyes] ; contains pills ; each array is scrybytes large, so far 3 * 4400 = ~ 13kB ; still gots plenty of xram .CSEG ; Interrupt Vector Table .ORG $000 ;Reset Address RJMP RESET .ORG INT0addr ;External Interrupt0 Vector Address RETI .ORG INT1addr ;External Interrupt1 Vector Address RETI .ORG ICP1addr ;Input Capture1 Interrupt Vector Address RETI .ORG OC1Aaddr ;Output Compare1A Interrupt Vector Address RJMP t1_cmpA .ORG OC1Baddr ;Output Compare1B Interrupt Vector Address RETI .ORG OVF1addr ;Overflow1 Interrupt Vector Address RETI .ORG OVF0addr ;Overflow0 Interrupt Vector Address RETI .ORG SPIaddr ;SPI Interrupt Vector Address RETI .ORG URXCaddr ;UART Receive Complete Interrupt Vector Address RETI .ORG UDREaddr ;UART Data Register Empty Interrupt Vector Address RETI .ORG UTXCaddr ;UART Transmit Complete Interrupt Vector Address RETI .ORG ACIaddr ;Analog Comparator Interrupt Vector Address RETI ; 11 x 8 bitmaps ; column groups are top, down, left, right ; row groups are closed, half, open ; compression possible: ; left and right are symmetrical about x axis ; left and right are opposite endian ; up and down are read in reverse line order ; all directions look the same closed ; IT LOOKS BACKWARDS (UP, DOWN, LEFT, RIGHT) BUT REMEMBER: LITTLE ENDIAN!!!!!!!! pacman_bmp: .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00011100, 0b00011100, 0b00011100, 0b00011100 .DB 0b00111110, 0b00111110, 0b00111110, 0b00111110 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b00111110, 0b00111110, 0b00111110, 0b00111110 .DB 0b00011100, 0b00011100, 0b00011100, 0b00011100 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00011100, 0b00011100, 0b00011100 .DB 0b00100010, 0b00111110, 0b00111110, 0b00111110 .DB 0b01100011, 0b01111111, 0b01111111, 0b01111111 .DB 0b01100011, 0b01111111, 0b01111100, 0b00011111 .DB 0b01110111, 0b01110111, 0b01110000, 0b00000111 .DB 0b01110111, 0b01110111, 0b01110000, 0b00000111 .DB 0b01111111, 0b01100011, 0b01111100, 0b00011111 .DB 0b01111111, 0b01100011, 0b01111111, 0b01111111 .DB 0b00111110, 0b00100010, 0b00111110, 0b00111110 .DB 0b00011100, 0b00000000, 0b00011100, 0b00011100 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00011100, 0b00011100, 0b00011100 .DB 0b00000000, 0b00111110, 0b00111110, 0b00111110 .DB 0b01000001, 0b01111111, 0b01111100, 0b00011111 .DB 0b01100011, 0b01111111, 0b01111000, 0b00001111 .DB 0b01110111, 0b01110111, 0b01110000, 0b00000111 .DB 0b01110111, 0b01110111, 0b01110000, 0b00000111 .DB 0b01111111, 0b01100011, 0b01111000, 0b00001111 .DB 0b01111111, 0b01000001, 0b01111100, 0b00011111 .DB 0b00111110, 0b00000000, 0b00111110, 0b00111110 .DB 0b00011100, 0b00000000, 0b00011100, 0b00011100 ghost_bmp: .DB 0b00011100, 0b00011100, 0b00011100, 0b00011100 .DB 0b00111110, 0b00111110, 0b00111110, 0b00111110 .DB 0b01101011, 0b01111111, 0b01111111, 0b01111111 .DB 0b01101011, 0b01111111, 0b01111111, 0b01111111 .DB 0b01101011, 0b01111111, 0b01110101, 0b01010111 .DB 0b01111111, 0b01111111, 0b01110101, 0b01010111 .DB 0b01111111, 0b01101011, 0b01110101, 0b01010111 .DB 0b01111111, 0b01101011, 0b01111111, 0b01111111 .DB 0b01111111, 0b01101011, 0b01111111, 0b01111111 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01010101, 0b01010101, 0b01010101, 0b01010101 .DB 0b00011100, 0b00011100, 0b00011100, 0b00011100 .DB 0b00111110, 0b00111110, 0b00111110, 0b00111110 .DB 0b01101011, 0b01111111, 0b01111111, 0b01111111 .DB 0b01101011, 0b01111111, 0b01111111, 0b01111111 .DB 0b01101011, 0b01111111, 0b01110101, 0b01010111 .DB 0b01111111, 0b01111111, 0b01110101, 0b01010111 .DB 0b01111111, 0b01101011, 0b01110101, 0b01010111 .DB 0b01111111, 0b01101011, 0b01111111, 0b01111111 .DB 0b01111111, 0b01101011, 0b01111111, 0b01111111 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b00101010, 0b00101010, 0b00101010, 0b00101010 .DB 0b00011100, 0b00011100, 0b00011100, 0b00011100 .DB 0b00111110, 0b00111110, 0b00111110, 0b00111110 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01101011, 0b01101011, 0b01101011, 0b01101011 .DB 0b01101011, 0b01101011, 0b01101011, 0b01101011 .DB 0b01101011, 0b01101011, 0b01101011, 0b01101011 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01101011, 0b01101011, 0b01101011, 0b01101011 .DB 0b01010101, 0b01010101, 0b01010101, 0b01010101 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01010101, 0b01010101, 0b01010101, 0b01010101 death_bmp: .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b01000001, 0b00000000, 0b00000000, 0b00000000 .DB 0b01100011, 0b01100011, 0b01000001, 0b00000000 .DB 0b01110111, 0b01110111, 0b01110111, 0b01110111 .DB 0b01110111, 0b01110111, 0b01110111, 0b01110111 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b00111110, 0b00111110, 0b00111110, 0b00111110 .DB 0b00011100, 0b00011100, 0b00011100, 0b00011100 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b01100011, 0b01000001, 0b00000000, 0b00000000 .DB 0b01110111, 0b01110111, 0b01110111, 0b00111110 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b00111110, 0b00111110, 0b00111110, 0b00111110 .DB 0b00011100, 0b00011100, 0b00011100, 0b00011100 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00011100, 0b00001000, 0b00001000, 0b00001000 .DB 0b01111111, 0b01111111, 0b00111110, 0b00011100 .DB 0b01111111, 0b01111111, 0b01111111, 0b01111111 .DB 0b00111110, 0b00111110, 0b00111110, 0b00111110 .DB 0b00011100, 0b00011100, 0b00011100, 0b00011100 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b00000000 .DB 0b00000000, 0b00000000, 0b00000000, 0b01001001 .DB 0b00000000, 0b00000000, 0b00000000, 0b00101010 .DB 0b00001000, 0b00001000, 0b00001000, 0b00000000 .DB 0b00011100, 0b00001000, 0b00001000, 0b01100011 .DB 0b00011100, 0b00011100, 0b00001000, 0b00000000 .DB 0b00111110, 0b00111110, 0b00111110, 0b00101010 .DB 0b00011100, 0b00011100, 0b00011100, 0b01001001 RESET: ; setup stack pointer (in internal sram) LDI temp, LOW(RAMEND) OUT SPL, temp LDI temp, HIGH(RAMEND) OUT SPH, temp ; interrupt every one NTSC line ; VERY IMPORTANT!!!!! LOAD HIGH BYTE FIRST, THEN LOW BYTE!!! ; OTHERWISE HIGH BYTE WILL BE ERASED!!! ; OCR1A = lineTime LDI temp, high(lineTime) OUT OCR1AH, temp LDI temp, low(lineTime) OUT OCR1AL, temp ; turn off pwm and oc lines ; TCCR1A = 0b00000000 CLR temp OUT TCCR1A, temp ; full speed, clear-on-match ; TCCR1B = 0b00001001 LDI temp, (1< XRAM ACCESS!!! ; XRAM ACCESS!!! WILL @%$# YOU UP OTHERWISE!!! ; MCUCR = 0b10100000; LDI temp, (1< 22 16 cycle pulse width (38 - 16 = 22 cycles avail) ; * TNN => 17 11 ; * NTN => 20 14 ; * NNT => 23 17 ; ******************************************************************************* ; save regs IN save, SREG ; load the sync and line values IN tempsync, sync IN templlo, lncntlo IN templhi, lncnthi ; PORTD.0 = syncON ; Be sure to preserve all other bits of PORTD ; sync holds syncON info IN temp, PORTD BST tempsync, 0 BLD temp, 0 OUT PORTD, temp ; Now begin cycle count; want ~4.7us pulse width (NO INTERLACING) ; linecount++ SUBI templlo, low(-1) SBCI templhi, high(-1) ; is linecount == 248? CPI templlo, low(248) ; 2 <= linecount <= 263 ; no need to check upper byte; only 248 has lower byte = 0xF8 ; linecount != 248 BRNE not248 ; linecount == 248 ; syncON = 1 ; syncOFF = 0 ; SER tempsync CLR tempsync COM tempsync RJMP not263 not248: ; is linecount == 251? CPI templlo, low(251) ; 2 <= linecount <= 263 ; no need to check upper byte; only 251 has lower byte = 0xFB ; linecount != 251 BRNE not251 ; linecount == 251 ; syncON = 0 ; syncOFF = 1 CLR tempsync RJMP not263 not251: ; is linecount == 263? CPI templlo, low(263) LDI temp, high(263) CPC templhi, temp ; 2 <= linecount <= 263 ; unfortunately need to check upper byte; both 263 and 7 have lower byte = 0x07 ; linecount != 263 BRNE not263 ; linecount == 263 ; linecount = 1 LDI templlo, low(1) LDI templhi, high(1) not263: ; PORTD.0 = syncOFF IN temp, PORTD ; need to output the non-sync signal now ; invert sync to get syncOFF COM tempsync BST tempsync, 0 BLD temp, 0 OUT PORTD, temp ; store the new sync and line values COM tempsync OUT sync, tempsync OUT lncntlo, templlo OUT lncnthi, templhi ; if ((LineCount >= (ScreenTop-1)) && (LineCount < ScreenBot)) ; Z = LineCount - (ScreenTop - 1) ; if Z < 0, branch to no raster SUBI templlo, low(ScreenTop - 1) SBCI templhi, high(ScreenTop - 1) BRLO zoom ; if Z >= (Height + 1), branch to no raster ; we compare with (Height + 1) instead of Height ; because the first(zeroth) line is actually just to ; initialize Y with the base pointer to screen ; works only if 0 <= (Height + 1) <= 255, otherwise need to compare ZH CPI templlo, low(Height + 1) BRSH zoom ; if (LineCount == (ScreenTop-1)) ; Zinitial = Screen ; we just have to make sure that Z (the bitmap byte pointer) is preserved over the raster ; might have to use a dedicated internal sram slot just to be safe CPI templlo, 0 BRNE load LDI ZL, low(screen) LDI ZH, high(screen) zoom: RJMP postraster load: ; v[0:Width] = screen[i+[0:Width]] ; *(Z + screen) = screen[Z] LD r0, Z+ LD r1, Z+ LD r2, Z+ LD r3, Z+ LD r4, Z+ LD r5, Z+ LD r6, Z+ LD r7, Z+ LD r8, Z+ LD r9, Z+ LD r10, Z+ LD r11, Z+ LD r12, Z+ LD r13, Z+ LD r14, Z+ LD r15, Z+ LD r16, Z+ LD r17, Z+ LD r18, Z+ LD r19, Z+ LD r20, Z+ LD r21, Z+ ; now blast them out to the screen ; PORTB.0 = v[0:Width].[0:7] (LITTLE-ENDIAN!) BLAST r0 BLAST r1 BLAST r2 BLAST r3 BLAST r4 BLAST r5 BLAST r6 BLAST r7 BLAST r8 BLAST r9 BLAST r10 BLAST r11 BLAST r12 BLAST r13 BLAST r14 BLAST r15 BLAST r16 BLAST r17 BLAST r18 BLAST r19 BLAST r20 BLAST r21 ; Now end the bitmap ; PORTB.0 = 0 ; rx = 0 since they are cleared everytime BLAST is called OUT PORTB, r21 ;################################################################################ postraster: postrasterline1: CPI templlo, low(Height + 1) BRNE postrasterline2 ; increment field counter IN temp, field INC temp OUT field, temp RJMP linecheckdone ;################################################################################ postrasterline2: CPI templlo, low(Height + 2) BRNE postrasterline3 IN temp, field CPI temp, 1 BREQ postrasterline2_oddfield LDI XL, low(ghost1_x) LDI XH, high(ghost1_x) LD temp_x, X+ LD temp_y, X+ LD temp_dir, X LDI drawwho, draw_ghost LDI which, 0b00000000 ; 2 lsb = 00 means erase first half RCALL dohalfbmp RJMP linecheckdone postrasterline2_oddfield: RJMP linecheckdone ;################################################################################ postrasterline3: CPI templlo, low(Height + 3) BRNE postrasterline4 IN temp, field CPI temp, 1 BREQ postrasterline3_oddfield LDI XL, low(ghost1_x) LDI XH, high(ghost1_x) LD temp_x, X+ LD temp_y, X+ LD temp_dir, X LDI drawwho, draw_ghost LDI which, 0b00000001 ; 2 lsb = 01 means erase second half RCALL dohalfbmp RJMP linecheckdone postrasterline3_oddfield: RJMP linecheckdone ;################################################################################ postrasterline4: CPI templlo, low(Height + 4) BRNE postrasterline5 IN temp, field CPI temp, 1 BREQ postrasterline4_oddfield LDI XL, low(ghost2_x) LDI XH, high(ghost2_x) LD temp_x, X+ LD temp_y, X+ LD temp_dir, X LDI drawwho, draw_ghost LDI which, 0b00000000 ; 2 lsb = 00 means erase first half RCALL dohalfbmp RJMP linecheckdone postrasterline4_oddfield: RJMP linecheckdone ;################################################################################ postrasterline5: CPI templlo, low(Height + 5) BRNE postrasterline10 IN temp, field CPI temp, 1 BREQ postrasterline5_oddfield LDI XL, low(ghost2_x) LDI XH, high(ghost2_x) LD temp_x, X+ LD temp_y, X+ LD temp_dir, X LDI drawwho, draw_ghost LDI which, 0b00000001 ; 2 lsb = 01 means erase second half RCALL dohalfbmp RJMP linecheckdone postrasterline5_oddfield: RJMP linecheckdone ;################################################################################ postrasterline10: CPI templlo, low(Height + 10) BRNE postrasterline11 IN temp, field CPI temp, 1 BREQ postrasterline10_oddfield IN temp_x, pacman_x IN temp_y, pacman_y IN temp_dir, pacman_direction LDI drawwho, draw_pacman LDI which, 0b00000000 ; 2 lsb = 00 means erase first half RCALL dohalfbmp RJMP linecheckdone postrasterline10_oddfield: RJMP linecheckdone ;################################################################################ postrasterline11: CPI templlo, low(Height + 11) BRNE postrasterline12 IN temp, field CPI temp, 1 BREQ postrasterline11_oddfield IN temp_x, pacman_x IN temp_y, pacman_y IN temp_dir, pacman_direction LDI drawwho, draw_pacman LDI which, 0b00000001 ; 2 lsb = 01 means erase second half RCALL dohalfbmp RJMP linecheckdone postrasterline11_oddfield: RJMP linecheckdone ;################################################################################ postrasterline12: CPI templlo, low(Height + 12) BRNE postrasterline13 ; output the score to the screen (big endian binary) LDI XL, low(pillsleft) LDI XH, high(pillsleft) LD horzPT, X+ LD vertPT, X LDI XL, low(screen) LDI XH, high(screen) SUBI XL, low(-scrbytes + 3) SBCI XH, high(-scrbytes + 3) LDI temp, 0b11111111 ST X+, temp ST X+, horzPT ST X, vertPT SBIW XL, Width + 2 LDI temp, 0b11111111 ST X+, temp LDI temp, 0b10101010 ST X+, temp ST X, temp IN temp, field SBRS temp, 0 ; skip if temp.0 is set RCALL pollandupdate ; thus we only draw pacman on even fields RJMP linecheckdone ;################################################################################ postrasterline13: CPI templlo, low(Height + 13) BRNE postrasterline14 IN temp, field CPI temp, 1 BREQ postrasterline13_oddfield IN temp_x, pacman_x IN temp_y, pacman_y IN temp_dir, pacman_direction LDI drawwho, draw_pacman LDI which, 0b00000010 ; 2 lsb = 10 means draw first half RCALL dohalfbmp RJMP linecheckdone postrasterline13_oddfield: RJMP linecheckdone ;################################################################################ postrasterline14: CPI templlo, low(Height + 14) BRNE postrasterline15 IN temp, field CPI temp, 1 BREQ postrasterline14_oddfield IN temp_x, pacman_x IN temp_y, pacman_y IN temp_dir, pacman_direction LDI drawwho, draw_pacman LDI which, 0b00000011 ; 2 lsb = 11 means draw second half RCALL dohalfbmp RJMP linecheckdone postrasterline14_oddfield: RJMP linecheckdone ;################################################################################ postrasterline15: CPI templlo, low(Height + 15) BRNE postrasterline16 IN temp, field CPI temp, 1 BREQ postrasterline15_oddfield LDI XL, low(ghost1_x) LDI XH, high(ghost1_x) LD temp_x, X+ LD temp_y, X+ LD temp_dir, X LDI drawwho, draw_ghost LDI which, 0b00000010 ; 2 lsb = 10 means draw first half RCALL dohalfbmp RJMP linecheckdone postrasterline15_oddfield: RJMP linecheckdone ;################################################################################ postrasterline16: CPI templlo, low(Height + 16) BRNE postrasterline17 IN temp, field CPI temp, 1 BREQ postrasterline16_oddfield LDI XL, low(ghost1_x) LDI XH, high(ghost1_x) LD temp_x, X+ LD temp_y, X+ LD temp_dir, X LDI drawwho, draw_ghost LDI which, 0b00000011 ; 2 lsb = 11 means draw second half RCALL dohalfbmp RJMP linecheckdone postrasterline16_oddfield: RJMP linecheckdone ;################################################################################ postrasterline17: CPI templlo, low(Height + 17) BRNE postrasterline18 IN temp, field CPI temp, 1 BREQ postrasterline17_oddfield LDI XL, low(ghost2_x) LDI XH, high(ghost2_x) LD temp_x, X+ LD temp_y, X+ LD temp_dir, X LDI drawwho, draw_ghost LDI which, 0b00000010 ; 2 lsb = 10 means draw first half RCALL dohalfbmp RJMP linecheckdone postrasterline17_oddfield: RJMP linecheckdone ;################################################################################ postrasterline18: CPI templlo, low(Height + 18) BRNE postrasterline23 IN temp, field CPI temp, 1 BREQ postrasterline18_oddfield LDI XL, low(ghost2_x) LDI XH, high(ghost2_x) LD temp_x, X+ LD temp_y, X+ LD temp_dir, X LDI drawwho, draw_ghost LDI which, 0b00000011 ; 2 lsb = 11 means draw second half RCALL dohalfbmp RJMP linecheckdone postrasterline18_oddfield: RJMP linecheckdone ;################################################################################ postrasterline23: linecheckdone: ; unsave regs OUT SREG, save RETI ; ###################################################################### ; # ; # Routine 'drawhorz' ; # ; # Draws a horizontal line on the specified array. ; # ; # Parameters ; # ---------- ; # (n) horzPT (gen reg) = y coordinate value of line ; # ; # (n) start (gen reg) = x value of line beginning ; # ; # (n) end (gen reg) = x value of line end ; # ; # (n) which (gen reg) = which array to draw to ; # ; ###################################################################### drawhorz: ; draw horizontal line in array ; this is a real hack, might have to stretch to 32 registers so ; multiplication is easy or get an MCU with a real multiplier ; take horzPT and multiply by Width to get the proper line offset ; use temp:horzPT to make addition happy CLR temp MOV offsetlo, horzPT CLR offsethi LSL offsetlo ROL offsethi LSL offsetlo ROL offsethi ADD offsetlo, horzPT ADC offsethi, temp LSL offsetlo ROL offsethi ADD offsetlo, horzPT ADC offsethi, temp LSL offsetlo ROL offsethi ; really terrible, a hardcoded method to multiply a byte by 22 in 15 cycles CPI which, screen_array BRNE _drawhorz_checkwhich LDI XL, low(screen) LDI XH, high(screen) RJMP _drawhorz_doneload _drawhorz_checkwhich: CPI which, walls_array BRNE _drawhorz_loadpills LDI XL, low(walls) LDI XH, high(walls) RJMP _drawhorz_doneload _drawhorz_loadpills: LDI XL, low(pills) LDI XH, high(pills) _drawhorz_doneload: ADD XL, offsetlo ADC XH, offsethi ; X now points to the first byte on the line at horzPT in the loaded array MOV offsetlo, start LSR offsetlo LSR offsetlo LSR offsetlo ; divide start by 8 to get horizontal start byte offset MOV byteoff, offsetlo ; want to start at the byte containing the start point CLR temp ADD XL, byteoff ADC XH, temp ; X now indexes the byte at the beginning of the actual to be drawn horizontal line MOV offsetlo, end LSR offsetlo LSR offsetlo LSR offsetlo ; divide start by 8 to get horizontal end byte offset MOV diff, offsetlo SUB diff, byteoff ; diff = (end byte) - (start byte) LDI temp, 1 CP diff, temp BRLO _drawhorz_drawonebyte ; if the difference between start and end bytes is == 0, then only draw one byte ; step 1: draw the first, 1-8 pixels white, byte ; SER mask1 CLR mask1 COM mask1 MOV cntr, start ANDI cntr, 0b00000111 ; AND with 0x07 to get bit offset within the byte BREQ _drawhorz_startshiftdone _drawhorz_startshift: LSL mask1 DEC cntr BRNE _drawhorz_startshift _drawhorz_startshiftdone: LD temp, X OR temp, mask1 ST X+, temp ; step 2: draw the full (all white) bytes of the line LDI temp, 2 CP diff, temp BRLO _drawhorz_fullloopdone SER temp DEC diff _drawhorz_fullloop: ST X+, temp DEC diff BRNE _drawhorz_fullloop _drawhorz_fullloopdone: ; step 3: draw the last, 1-8 pixels white, byte ; SER mask1 CLR mask1 COM mask1 MOV cntr, end ANDI cntr, 0b00000111 ; AND with 0x07 to get bit offset within the byte SUBI cntr, 7 ; get bitoff - 7 NEG cntr ; get 7 - bitoff BREQ _drawhorz_endshiftdone _drawhorz_endshift: LSR mask1 ; little-endian!!! DEC cntr BRNE _drawhorz_endshift _drawhorz_endshiftdone: LD temp, X OR temp, mask1 ST X, temp RJMP _drawhorz_drawdone _drawhorz_drawonebyte: ; if only drawing one byte, take a full bitmask and shift right by the start offset ; shift back left by the same amount and then shift left again by the 7-(end offset). ; shift back right by the same amount to get the proper bitmask. ; alternatively, do this with a second mask and and it with the first. ; SER mask1 CLR mask1 COM mask1 SER temp MOV cntr, start ANDI cntr, 0b00000111 ; AND with 0x07 to get bit offset within the byte BREQ _drawhorz_startshift2done _drawhorz_startshift2: LSL temp ; little-endian!!! DEC cntr BRNE _drawhorz_startshift2 _drawhorz_startshift2done: AND mask1, temp SER temp MOV cntr, end ANDI cntr, 0b00000111 ; AND with 0x07 to get bit offset within the byte SUBI cntr, 7 ; get bitoff - 7 NEG cntr ; get 7 - bitoff BREQ _drawhorz_endshift2done _drawhorz_endshift2: LSR temp ; little-endian!!! DEC cntr BRNE _drawhorz_endshift2 _drawhorz_endshift2done: AND mask1, temp ; now draw this one byte horizontal line in LD temp, X OR temp, mask1 ST X, temp _drawhorz_drawdone: RET ; end drawhorz routine ; ###################################################################### ; # ; # Routine 'drawvert' ; # ; # Draws a vertical line on the specified array. ; # ; # Parameters ; # ---------- ; # (n) vertPT (gen reg) = x coordinate value of line ; # ; # (n) start (gen reg) = y value of line beginning ; # ; # (n) end (gen reg) = y value of line end ; # ; # (n) which (gen reg) = which array to draw to ; # ; ###################################################################### drawvert: MOV offsetlo, vertPT CLR offsethi ; might not need offsethi since so far only need byte to index all pixels across LSR offsetlo LSR offsetlo LSR offsetlo ; divide by 8 to get the byte offset that the line is in CPI which, screen_array BRNE _drawvert_checkwhich LDI XL, low(screen) LDI XH, high(screen) RJMP _drawvert_doneload _drawvert_checkwhich: CPI which, walls_array BRNE _drawvert_loadpills LDI XL, low(walls) LDI XH, high(walls) RJMP _drawvert_doneload _drawvert_loadpills: LDI XL, low(pills) LDI XH, high(pills) _drawvert_doneload: ADD XL, offsetlo ADC XH, offsethi LDI temp, 0b00000001 MOV mask1, temp MOV cntr, vertPT ANDI cntr, 0b00000111 ; AND with 0x07 to get bit offset within the byte BREQ _drawvert_shiftdone ; accidentally branched to vertloop1, made drawing vertPT = 0 impossible _drawvert_shiftmask: LSL mask1 DEC cntr BRNE _drawvert_shiftmask _drawvert_shiftdone: OR cntr, start BREQ _drawvert_lineoffsetdone _drawvert_lineoffset: ADIW XL, Width DEC cntr BRNE _drawvert_lineoffset _drawvert_lineoffsetdone: MOV cntr, end SUB cntr, start INC cntr _drawvert_loop1: LD temp, X OR temp, mask1 ST X, temp ADIW XL, Width DEC cntr BRNE _drawvert_loop1 RET ; end drawvert routine ; ###################################################################### ; # ; # Routine 'go_to_starting_positions' ; # ; # Resets ; # ; # Parameters ; # ---------- ; # (d) field (gen reg) = field counter ; # ; # (d) startgame (RAM var) = flags start of game ; # ; # (d) deathcounter (RAM var) = tracks death status of Pac-Man ; # ; # (d) mod3 (I/O reg) = current bitmap of pacman ; # ; # (d) ghostcntr (I/O reg) = current bitmap of ghosts ; # ; # (d) pacman_x (I/O reg) = x coordinate of pacman ; # ; # (d) pacman_y (I/O reg) = y coordinate of pacman ; # ; # (d) pacman_dir (I/O reg) = direction of pacman ; # ; # (d) ghost1_x (RAM var) = coordinate and direction info for ghost1 ; # ; # (d) ghost2_x (RAM var) = coordinate and direction info for ghost2 ; # ; ###################################################################### go_to_starting_positions: ; initialize the flags and screen and whatnot ; initialize field to 0 CLR temp OUT field, temp LDI XL, low(startgame) LDI XH, high(startgame) CLR temp ST X, temp LDI XL, low(deathcounter) LDI XH, high(deathcounter) CLR temp ST X, temp LDI temp, 0 OUT mod3, temp LDI temp, 1 OUT ghostcntr, temp ; pacman init LDI temp, 71 MOV temp_x, temp LDI temp, 126 MOV temp_y, temp LDI temp, 2 ; initial direction is left MOV temp_dir, temp OUT pacman_x, temp_x OUT pacman_y, temp_y OUT pacman_direction, temp_dir ; ghost1 init LDI temp, 71 MOV temp_x, temp LDI temp, 62 MOV temp_y, temp LDI temp, 2 ; initial direction is left MOV temp_dir, temp LDI XL, low(ghost1_x) LDI XH, high(ghost1_x) ST X+, temp_x ST X+, temp_y ST X, temp_dir ; ghost2 init LDI temp, 59 MOV temp_x, temp LDI temp, 78 MOV temp_y, temp LDI temp, 0 ; initial direction is up MOV temp_dir, temp LDI XL, low(ghost2_x) LDI XH, high(ghost2_x) ST X+, temp_x ST X+, temp_y ST X, temp_dir RET ; ###################################################################### ; # ; # Routine 'drawvertpills' ; # ; # Draws a vertical line of pills as a group of 2x2 horizontal ; # lines to both screen array and pills array so that pills are seen ; # and detected. ; # ; # Parameters ; # ---------- ; # (c) horzPT (gen reg) = y value of upper left corner of first pill ; # ; # (c) cntr3 (I/O reg) = number of pills to draw ; # ; # (n) start (gen reg) = x value of pills' left pixel ; # ; # (d) end (gen reg) = x value of pills' right pixel ; # ; ###################################################################### drawvertpills: MOV end, start INC end _drawvertpills_loop: LDI which, screen_array RCALL drawhorz LDI which, pills_array RCALL drawhorz INC horzPT LDI which, screen_array RCALL drawhorz LDI which, pills_array RCALL drawhorz SUBI horzPT, -7 IN temp, cntr3 DEC temp OUT cntr3, temp BRNE _drawvertpills_loop RET ; ###################################################################### ; # ; # Routine 'drawhorzpills' ; # ; # Draws a horizontal line of pills as a group of 2x2 horizontal ; # lines to both screen array and pills array so that pills are seen ; # and detected. ; # ; # Parameters ; # ---------- ; # (c) horzPT (gen reg) = y value of upper left corner of first pill ; # ; # (c) cntr3 (I/O reg) = number of pills to draw ; # ; # (n) start (gen reg) = x value of pills' left pixel ; # ; # (d) end (gen reg) = x value of pills' right pixel ; # ; ###################################################################### drawhorzpills: MOV end, start INC end _drawhorzpills_loop: LDI which, screen_array RCALL drawhorz LDI which, pills_array RCALL drawhorz INC horzPT LDI which, screen_array RCALL drawhorz LDI which, pills_array RCALL drawhorz DEC horzPT SUBI start, -4 SUBI end, -4 IN temp, cntr3 DEC temp OUT cntr3, temp BRNE _drawhorzpills_loop RET ; ###################################################################### ; # ; # Routine 'dohalfbmp' ; # ; # Processes half a bitmap as specified. ; # ; # Parameters ; # ---------- ; # (n) temp_x (gen reg) = x coordinate of position pixel ; # ; # (n) temp_y (gen reg) = y coordinate of position pixel ; # ; # (n) temp_dir (gen reg) = direction of sprite (0 for dying Pac-Man) ; # ; # (n) drawwho (gen reg) = specifies the bitmap to be drawn ; # ; # (n) deathcounter (RAM var) = specifies death status of Pac-Man ; # ; # (n) which (gen reg) = specifies job to do and which part to do ; # 00 = erase upper half ; # 01 = erase lower half ; # 10 = draw upper half ; # 11 = draw lower half ; # ; ###################################################################### dohalfbmp: ; divide temp_x by 8 to get horizontal byte offset MOV offsetlo, temp_x LSR offsetlo LSR offsetlo LSR offsetlo MOV byteoff, offsetlo ; multiply temp_y by 22(Width) to get vertical line offset ; need to do a software multiply until a better chip is obtained CLR temp MOV offsetlo, temp_y CLR offsethi LSL offsetlo ROL offsethi LSL offsetlo ROL offsethi ADD offsetlo, temp_y ADC offsethi, temp LSL offsetlo ROL offsethi ADD offsetlo, temp_y ADC offsethi, temp LSL offsetlo ROL offsethi ; offsethi:offsetlo points to the byte in the three game arrays ; containing the position pixel of the bitmap ADD offsetlo, byteoff ADC offsethi, temp ; point X to screen[position byte] LDI XL, low(screen) LDI XH, high(screen) ADD XL, offsetlo ADC XH, offsethi ; point Y to pills[position byte] LDI YL, low(pills) LDI YH, high(pills) ADD YL, offsetlo ADC YH, offsethi ; generate a mask to mask out the bitmap ; need to load proper bitmap from flash ; don't draw ghosts if pacman is in death act II LDI ZL, low(deathcounter) LDI ZH, high(deathcounter) LD cntr, Z CPI cntr, end_actI + 1 BRLO _dohalfbmp_loadbmpstart CPI drawwho, draw_ghost BRNE _dohalfbmp_loaddeathbmp RJMP _dohalfbmp_end _dohalfbmp_loadbmpstart: CPI drawwho, draw_pacman BRNE _dohalfbmp_loadghostbmpbase LDI ZL, low(pacman_bmp * 2) LDI ZH, high(pacman_bmp * 2) IN temp, mod3 RJMP _dohalfbmp_gotbmpbase _dohalfbmp_loadghostbmpbase: LDI ZL, low(ghost_bmp * 2) LDI ZH, high(ghost_bmp * 2) IN temp, ghostcntr RJMP _dohalfbmp_gotbmpbase _dohalfbmp_loaddeathbmp: ; will draw more bitmaps until cntr gets to end_actII CPI cntr, end_actII + 1 BRLO _dohalfbmp_loaddeathbmp2 RJMP _dohalfbmp_end _dohalfbmp_loaddeathbmp2: ; only way to get here is is the deathcounter is >= end_actI + 1 and < end_actII + 1 LDI ZL, low(death_bmp * 2) LDI ZH, high(death_bmp * 2) SUBI cntr, end_actI + 1 MOV temp, cntr ANDI cntr, 0b00000011 ADD ZL, cntr CLR cntr ADC ZH, cntr ; now Z points to the correct column of death LSR temp LSR temp ; now temp is how many rows down to go _dohalfbmp_gotbmpbase: ; check the state of the object and retrieve the proper bitmap TST temp BREQ _dohalfbmp_gotbmpstate _dohalfbmp_nextbmpstate: ADIW ZL, 4 * 11 ; goto next state DEC temp BRNE _dohalfbmp_nextbmpstate _dohalfbmp_gotbmpstate: ; point to the bitmap showing the current direction CLR temp ADD ZL, temp_dir ADC ZH, temp ; start the outer loop counter at 6 ; this will process 6 lines of the bitmap ; also, point to the middle of the bitmap, where the ; second half would begin drawing LDI cntr2, 6 ADIW ZL, 5 * 4 ; check if drawing first or second half of bitmap MOV temp, which ANDI temp, 0b00000001 ; which.1 sez first or second half BRNE _dohalfbmp_drawingisoffset ; start 5 lines above position pixel SUBI XL, low(5 * Width) SBCI XH, high(5 * Width) SUBI YL, low(5 * Width) SBCI YH, high(5 * Width) ; want to process 5 lines in first half DEC cntr2 ; want to begin drawing from the top of bitmap SBIW ZL, 5 * 4 _dohalfbmp_drawingisoffset: ; check the position pixel's bit offset MOV cntr, temp_x ANDI cntr, 0b00000111 SUBI cntr, 4 ; if bit offset >= 4, X and Y are pointing to the correct byte BRGE _dohalfbmp_process ; otherwise, have X and Y point to the next left byte SBIW XL, 1 SBIW YL, 1 _dohalfbmp_process: ; load the bitmap LPM ; temp0 = bitmap at the current line MOV mask1, temp0 CLR mask2 MOV cntr, temp_x ANDI cntr, 0b00000111 ; AND with 0x07 to get bit offset within the byte SUBI cntr, 4 BRGE _dohalfbmp_premaskshiftleft ; if the bit offset is < 4, need to shift bitmap to the right NEG cntr EOR mask1, mask2 EOR mask2, mask1 EOR mask1, mask2 ; swap mask1 and mask2 _dohalfbmp_maskshiftright: LSR mask2 ; remember, little-endian drawing ROR mask1 DEC cntr BRNE _dohalfbmp_maskshiftright RJMP _dohalfbmp_maskshiftdone _dohalfbmp_premaskshiftleft: ; if the bit offset is exactly 4, we are finished shifting BREQ _dohalfbmp_maskshiftdone _dohalfbmp_maskshiftleft: LSL mask1 ; remember, little-endian drawing ROL mask2 DEC cntr BRNE _dohalfbmp_maskshiftleft _dohalfbmp_maskshiftdone: MOV temp, which ANDI temp, 0b00000010 ; which.1 sez to erase or draw BRNE _dohalfbmp_draw _dohalfbmp_erase: LD temp, X ; temp = screen[position's byte] COM mask1 AND temp, mask1 ; bang! bitmap gone LD temp0, Y+ OR temp, temp0 ; restore any dots ST X+, temp LD temp, X ; temp = screen[position's right byte] COM mask2 AND temp, mask2 ; bang! bitmap gone LD temp0, Y OR temp, temp0 ; restore any dots ST X, temp ADIW XL, Width - 1 ; goto the next line of the screen ADIW YL, Width - 1 ; goto the next line of the pills ADIW ZL, 4 ; goto the next line of the bitmap DEC cntr2 BRNE _dohalfbmp_process RJMP _dohalfbmp_end _dohalfbmp_draw: LD temp, X ; temp = screen[position's byte] OR temp, mask1 ; bang! bitmap drawn ST X+, temp LD temp, X ; temp = screen[position's right byte] OR temp, mask2 ; bang! bitmap drawn ST X, temp ADIW XL, Width - 1 ; goto the next line of the screen ADIW ZL, 4 ; goto the next line of the bitmap DEC cntr2 BRNE _dohalfbmp_process _dohalfbmp_end: RET ; end _dohalfbmp ; ###################################################################### ; # ; # Procedure 'pollandupdate' ; # ; # Polls buttons for LEGAL changes in the direction of Pac-Man. ; # Updates Pac-Man's position accordingly. Also implements ghost AI ; # and updates their position. ; # ; # Parameters ; # ---------- ; # accesses and updates all sprites' info, all counters and status flags. ; # ; ###################################################################### pollandupdate: LDI XL, low(startgame) LDI XH, high(startgame) LD temp, X CPI temp, 1 BREQ _pollandupdate_start ; if game has started, do stuff SBIC PIND, 5 ; else check for button press RJMP _pollandupdate_end ; freeze! LDI temp, 1 ST X, temp _pollandupdate_start: IN temp, field ANDI temp, 0b00001111 ; check if this is a 16th field BRNE _pollandupdate_not16th IN temp, ghostcntr INC temp ANDI temp, 0b00000001 OUT ghostcntr, temp _pollandupdate_not16th: ; this branch makes sure that we update the bitmap states ; only once every 4 fields ; i.e. once every 2 drawn fields ; otherwise bitmaps change too fast LDI XL, low(deathcounter) LDI XH, high(deathcounter) LD cntr, X CPI cntr, 0 BREQ _pollandupdate_incmod3 ; if pacman is dying, do not change his mod3 RJMP _pollandupdate_dying _pollandupdate_incmod3: IN temp, field ANDI temp, 0b00000011 ; check if this is a 4th field BRNE _pollandupdate_normal IN temp, mod3 INC temp CPI temp, 3 BRNE _pollandupdate_store_mod3 CLR temp _pollandupdate_store_mod3: OUT mod3, temp ; store the remainder for later ; even though updates on mod3 and ghostcntr done here ; pollandupdate has the final say on the state according ; to whether pacman has hit a wall or eaten a megapill _pollandupdate_normal: ; get the current position and direction of pacman IN temp_x, pacman_x IN temp_y, pacman_y IN temp_dir, pacman_direction ;3 ; erase pacman at this position ; calculate the screen byte containing pacman ; remember, (pixel's byte) = (px / (pixels per byte)) + (py * (bytes per line)) ; get pacman's byte offset MOV offsetlo, temp_x LSR offsetlo LSR offsetlo LSR offsetlo ; divide x by 8 to get horizontal byte offset MOV byteoff, offsetlo ;3 + 5 ; get line offset of pacman CLR temp MOV offsetlo, temp_y CLR offsethi LSL offsetlo ROL offsethi LSL offsetlo ROL offsethi ADD offsetlo, temp_y ADC offsethi, temp LSL offsetlo ROL offsethi ADD offsetlo, temp_y ADC offsethi, temp LSL offsetlo ROL offsethi ;3 + 5 + 15 ; really terrible, a hardcoded method to multiply a byte by 22 in 15 cycles ; offsetlo:offsethi points to the screen byte containing pacman (if screen were at 0) ADD offsetlo, byteoff ADC offsethi, temp LDI YL, low(walls) LDI YH, high(walls) LDI ZL, low(pills) LDI ZH, high(pills) ADD YL, offsetlo ADC YH, offsethi ; Y points to walls[pacman's byte] ADD ZL, offsetlo ADC ZH, offsethi ; Z points to pills[pacman's byte] LDI temp, 0b00000001 MOV mask3, temp MOV cntr, temp_x ANDI cntr, 0b00000111 ; AND with 0x07 to get bit offset within the byte ; actually, we are doing px % (pixels per byte) ; but since pixels per byte is 8 and 8 is a power ; of 2, we can do px & (8 - 1) for mod BREQ _pollandupdate_maskshiftdone ; the ANDI did an implicit compare with zero ;3 + 5 + 15 + 15w _pollandupdate_maskshift: LSL mask3 ; single bit mask useful for horizontal wall detection DEC cntr BRNE _pollandupdate_maskshift ;3 + 5 + 15 + 15w + 27w _pollandupdate_maskshiftdone: ; mask3 indicates pacman's position so be sure to preserve ; draw pacman at the new position ; step 1: get new direction ;**************************************************************************************************** _pollandupdate_button4check: ; is the up button pressed? (button 4) ; if PIND.4 = 1, then the button has not been pressed ; go to next instruction which jumps to next button detect SBIC PIND, 4 RJMP _pollandupdate_button3check ; button 4 isn't pressed, check button 3 ; check for above wall SBIW YL, Width ; point to byte above pacman in walls (careful! what you subtract to) LD temp, Y ADIW YL, Width ; undo change to Y AND temp, mask3 BRNE _pollandupdate_button3check ; pacman is blocked! LDI temp, 0 MOV temp_dir, temp ;**************************************************************************************************** ;3 + 5 + 15 + 15w + 27w + 13w ;**************************************************************************************************** _pollandupdate_button3check: ; down = 3 SBIC PIND, 3 RJMP _pollandupdate_button2check ; check for below wall ADIW YL, Width ; point to byte below pacman in walls (careful! what you add to) LD temp, Y SBIW YL, Width ; undo change to Y AND temp, mask3 BRNE _pollandupdate_button2check ; pacman is blocked! LDI temp, 1 MOV temp_dir, temp ;**************************************************************************************************** ;3 + 5 + 15 + 15w + 27w + 13w + 13w ;**************************************************************************************************** _pollandupdate_button2check: ; left = 2 SBIC PIND, 2 RJMP _pollandupdate_button1check ; check for left wall MOV templlo, mask3 LSR templlo ; templlo now masks pixel to pacman's left (little-endian!!!!!!) BRNE _pollandupdate_checkleft1 ROR templlo ; mask the rightmost bit in the byte to pacman's left SBIW YL, 1 ; point to next left byte _pollandupdate_checkleft1: LD temp, Y AND temp, templlo ; check if there is a wall BRNE _pollandupdate_undocheckleft1 ; pacman is blocked! LDI temp, 2 MOV temp_dir, temp _pollandupdate_undocheckleft1: LSL templlo ; undo change to templlo to see if need to undo a point to left byte BRNE _pollandupdate_button1check ADIW YL, 1 ; undo point to next left byte ;**************************************************************************************************** ;3 + 5 + 15 + 15w + 27w + 13w + 13w + 19w ;**************************************************************************************************** _pollandupdate_button1check: ; right = 1 SBIC PIND, 1 RJMP _pollandupdate_directioncheck ; check for right wall MOV templlo, mask3 LSL templlo ; templlo now masks pixel to pacman's right (little-endian!!!!!!) BRNE _pollandupdate_checkright1 ROL templlo ; mask the leftmost bit in the byte to pacman's right ADIW YL, 1 ; point to the byte right of pacman's _pollandupdate_checkright1: LD temp, Y AND temp, templlo ; check if there is a wall BRNE _pollandupdate_undocheckright1; pacman is blocked! LDI temp, 3 MOV temp_dir, temp _pollandupdate_undocheckright1: LSR templlo BRNE _pollandupdate_directioncheck SBIW YL, 1 ; undo point to next right byte ;**************************************************************************************************** ;3 + 5 + 15 + 15w + 27w + 13w + 13w + 19w + 19w ; = 129 (looking good!) _pollandupdate_directioncheck: LDI temp, 0 CP temp_dir, temp BREQ _pollandupdate_updateup LDI temp, 1 CP temp_dir, temp BREQ _pollandupdate_updatedown LDI temp, 2 CP temp_dir, temp BREQ _pollandupdate_updateleft LDI temp, 3 CP temp_dir, temp BREQ _pollandupdate_updateright RJMP crap ;129 + 4 to jump here ;**************************************************************************************************** _pollandupdate_updateup: ; check for above wall SBIW YL, Width ; point to byte above pacman in walls (careful! what you subtract to) SBIW ZL, Width ; go up one line in pill array LD temp, Y AND temp, mask3 BRNE _pollandupdate_blocked ; pacman is blocked! DEC temp_y ; pacman is OK to go up LD temp, Z AND temp, mask3 BREQ _pollandupdate_updatevars LDI killpilldir, -1 RCALL killpillvert RJMP _pollandupdate_updatevars ;**************************************************************************************************** ;129 + 7 to jump here ;**************************************************************************************************** _pollandupdate_updatedown: ; check for below wall ADIW YL, Width ; point to byte below pacman in walls (careful! what you add to) ADIW ZL, Width ; go down one line in pill array LD temp, Y AND temp, mask3 BRNE _pollandupdate_blocked ; pacman is blocked! INC temp_y ; pacman is OK to go down LD temp, Z AND temp, mask3 BREQ _pollandupdate_updatevars LDI killpilldir, 1 RCALL killpillvert RJMP _pollandupdate_updatevars ;**************************************************************************************************** ;129 + 10 to jump here ;**************************************************************************************************** _pollandupdate_updateleft: ; check for left wall MOV templlo, mask3 LSR templlo ; templlo now masks pixel to pacman's left (little-endian!!!!!!) BRNE _pollandupdate_checkleft2 ROR templlo ; mask the rightmost bit in the byte to pacman's left SBIW YL, 1 ; point to next left byte SBIW ZL, 1 ; point to next left byte in pills _pollandupdate_checkleft2: LD temp, Y AND temp, templlo ; check if there is a wall BRNE _pollandupdate_blocked ; pacman is blocked! ; the actual task of moving pacman ; altering horizontal byte focus is a bit harder ; need to check the internal bit offset ; first decrement bitoff ; if bitoff == -1, need to focus 1 byte left and bitoff &= 7, else do nothing DEC temp_x ; pacman is OK to go left MOV mask3, templlo LD temp, Z AND temp, mask3 BREQ _pollandupdate_updatevars LDI killpilldir, -1 RCALL killpillhorz RJMP _pollandupdate_updatevars ;**************************************************************************************************** ;129 + 13 to jump here ;**************************************************************************************************** _pollandupdate_updateright: ; check for right wall MOV templlo, mask3 LSL templlo ; templlo now masks pixel to pacman's right (little-endian!!!!!!) BRNE _pollandupdate_checkright2 ROL templlo ; mask the leftmost bit in the byte to pacman's right ADIW YL, 1 ; point to the byte right of pacman's ADIW ZL, 1 ; point to next left byte in pills _pollandupdate_checkright2: LD temp, Y AND temp, templlo ; check if there is a wall BRNE _pollandupdate_blocked ; pacman is blocked! ; the actual task of moving pacman ; altering horizontal byte focus is a bit harder ; need to check the internal bit offset ; first increment bitoff ; if bitoff == 8, need to focus 1 byte right and bitoff = 0, else do nothing INC temp_x ; pacman is OK to go right MOV mask3, templlo LD temp, Z AND temp, mask3 BREQ _pollandupdate_updatevars LDI killpilldir, 1 RCALL killpillhorz RJMP _pollandupdate_updatevars ;**************************************************************************************************** _pollandupdate_blocked: LDI temp, 2 OUT mod3, temp ; this makes pacman stay in the open mouth state if blocked _pollandupdate_updatevars: ; store the new position and direction of pacman OUT pacman_x, temp_x OUT pacman_y, temp_y OUT pacman_direction, temp_dir _pollandupdate_g1hitcheck: ; check for collision with ghost1 LDI XL, low(ghost1_x) LDI XH, high(ghost1_x) LD temp, X+ ; temp has the x coordinate of ghost1 SUBI temp, 4 ; now temp is leftmost reach of ghost CP temp_x, temp BRLO _pollandupdate_g2hitcheck SUBI temp, -4 - 4 ; now temp is rightmost reach + 1 of ghost CP temp_x, temp BRSH _pollandupdate_g2hitcheck ; if we get this far, we have established that pacman is in the ; same x interval as the ghost LD temp, X ; temp has the y coordinate of ghost1 SUBI temp, 5 ; now temp is uppermost reach of ghost CP temp_y, temp BRLO _pollandupdate_g2hitcheck SUBI temp, -5 - 7 ; now temp is lowermost reach + 1 of ghost CP temp_y, temp BRSH _pollandupdate_g2hitcheck RJMP _pollandupdate_gcollided _pollandupdate_g2hitcheck: LDI XL, low(ghost2_x) LDI XH, high(ghost2_x) LD temp, X+ SUBI temp, 4 CP temp_x, temp BRLO _pollandupdate_moveghostinit SUBI temp, -4 - 4 CP temp_x, temp BRSH _pollandupdate_moveghostinit LD temp, X SUBI temp, 5 CP temp_y, temp BRLO _pollandupdate_moveghostinit SUBI temp, -5 - 7 CP temp_y, temp BRSH _pollandupdate_moveghostinit RJMP _pollandupdate_gcollided _pollandupdate_gcollided: ; if we get this far, we know that there has been a ghost collision LDI XL, low(deathcounter) LDI XH, high(deathcounter) LD temp, X INC temp ST X, temp ; pacman is now in shock RJMP _pollandupdate_end _pollandupdate_moveghostinit: MOV horzPT, temp_x ; save pacman's position MOV vertPT, temp_y LDI XL, low(ghost1_x) LDI XH, high(ghost1_x) LDI cntr2, 2 _pollandupdate_moveghost: LD temp_x, X+ LD temp_y, X+ LD temp_dir, X RCALL xy_to_pointermask SBRC cntr2, 1 RJMP _pollandupdate_movefastghost IN temp, field SBRC temp, 2 RJMP _pollandupdate_endghost _pollandupdate_movefastghost: LDI YL, low(walls) LDI YH, high(walls) ADD YL, offsetlo ADC YH, offsethi ; Y points to walls[ghost's byte] ;************************************************** _pollandupdate_ghostlastup: LDI temp, 0 CP temp_dir, temp BRNE _pollandupdate_ghostlastdown RCALL checkupside MOV cantagain, blocked CP temp_x, horzPT BRLO _pollandupdate_upwantright RCALL checkleftside TST blocked BRNE _pollandupdate_lastupnoleft DEC temp_x LDI temp, 2 MOV temp_dir, temp RJMP _pollandupdate_endghost _pollandupdate_lastupnoleft: TST cantagain BRNE _pollandupdate_forceright DEC temp_y RJMP _pollandupdate_endghost _pollandupdate_upwantright: RCALL checkrightside TST blocked BRNE _pollandupdate_lastupnoright INC temp_x LDI temp, 3 MOV temp_dir, temp RJMP _pollandupdate_endghost _pollandupdate_lastupnoright: TST cantagain BRNE _pollandupdate_forceleft DEC temp_y RJMP _pollandupdate_endghost ;************************************************** _pollandupdate_ghostlastdown: LDI temp, 1 CP temp_dir, temp BRNE _pollandupdate_ghostlastleft RCALL checkdownside MOV cantagain, blocked CP temp_x, horzPT BRLO _pollandupdate_downwantright RCALL checkleftside TST blocked BRNE _pollandupdate_lastdownnoleft DEC temp_x LDI temp, 2 MOV temp_dir, temp RJMP _pollandupdate_endghost _pollandupdate_lastdownnoleft: TST cantagain BRNE _pollandupdate_forceright INC temp_y RJMP _pollandupdate_endghost _pollandupdate_downwantright: RCALL checkrightside TST blocked BRNE _pollandupdate_lastdownnoright INC temp_x LDI temp, 3 MOV temp_dir, temp RJMP _pollandupdate_endghost _pollandupdate_lastdownnoright: TST cantagain BRNE _pollandupdate_forceleft INC temp_y RJMP _pollandupdate_endghost ;************************************************** _pollandupdate_forceleft: DEC temp_x LDI temp, 2 MOV temp_dir, temp RJMP _pollandupdate_endghost _pollandupdate_forceright: INC temp_x LDI temp, 3 MOV temp_dir, temp RJMP _pollandupdate_endghost ;************************************************** _pollandupdate_ghostlastleft: LDI temp, 2 CP temp_dir, temp BRNE _pollandupdate_ghostlastright RCALL checkleftside MOV cantagain, blocked CP temp_y, vertPT BRLO _pollandupdate_leftwantdown RCALL checkupside TST blocked BRNE _pollandupdate_lastleftnoup DEC temp_y LDI temp, 0 MOV temp_dir, temp RJMP _pollandupdate_endghost _pollandupdate_lastleftnoup: TST cantagain BRNE _pollandupdate_forcedown DEC temp_x RJMP _pollandupdate_endghost _pollandupdate_leftwantdown: RCALL checkdownside TST blocked BRNE _pollandupdate_lastleftnodown INC temp_y LDI temp, 1 MOV temp_dir, temp RJMP _pollandupdate_endghost _pollandupdate_lastleftnodown: TST cantagain BRNE _pollandupdate_forceup DEC temp_x RJMP _pollandupdate_endghost ;************************************************** _pollandupdate_ghostlastright: RCALL checkrightside MOV cantagain, blocked CP temp_y, vertPT BRLO _pollandupdate_rightwantdown RCALL checkupside TST blocked BRNE _pollandupdate_lastrightnoup DEC temp_y LDI temp, 0 MOV temp_dir, temp RJMP _pollandupdate_endghost _pollandupdate_lastrightnoup: TST cantagain BRNE _pollandupdate_forcedown INC temp_x RJMP _pollandupdate_endghost _pollandupdate_rightwantdown: RCALL checkdownside TST blocked BRNE _pollandupdate_lastrightnodown INC temp_y LDI temp, 1 MOV temp_dir, temp RJMP _pollandupdate_endghost _pollandupdate_lastrightnodown: TST cantagain BRNE _pollandupdate_forceup INC temp_x RJMP _pollandupdate_endghost ;************************************************** _pollandupdate_forceup: DEC temp_y LDI temp, 0 MOV temp_dir, temp RJMP _pollandupdate_endghost _pollandupdate_forcedown: INC temp_y LDI temp, 1 MOV temp_dir, temp RJMP _pollandupdate_endghost ;************************************************** _pollandupdate_endghost: SBIW XL, 2 ST X+, temp_x ST X+, temp_y ST X+, temp_dir DEC cntr2 BREQ _pollandupdate_end RJMP _pollandupdate_moveghost _pollandupdate_dying: IN temp, field ANDI temp, 0b00000111 BRNE _pollandupdate_end CPI cntr, end_actI + 1 BRNE _pollandupdate_updatecntrs ; if this is the first count of death act II, change direction to 0 LDI temp, 0 MOV temp_dir, temp ; direction offset should be zeroed during death OUT pacman_direction, temp_dir _pollandupdate_updatecntrs: INC cntr ; increment the death counter every ; eight fields (four frames) CPI cntr, end_actIII BRNE _pollandupdate_stilldying ; when the death counter reaches end_actIII, reset positions RCALL go_to_starting_positions RJMP _pollandupdate_end _pollandupdate_stilldying: ST X, cntr _pollandupdate_end: RET ; end pollandupdate ; ###################################################################### ; # ; # Routine 'check(x)side' ; # ; # Four routines which check a position pixel against the next ; # pixel over in the walls array to see if a move in that direction ; # is legal. ; # ; # Parameters ; # ---------- ; # (n) mask3 (lo reg) = pixel's bitmask ; # ; # (c) temp (hi reg) = temporary register ; # ; # (c) templlo (hi reg) = temporary register ; # ; # (d) blocked (hi reg) = blocked flag ; # ; ###################################################################### checkupside: SER blocked SBIW YL, Width LD temp, Y ADIW YL, Width ; restore Y AND temp, mask3 BRNE _checkupside_end CLR blocked _checkupside_end: RET checkdownside: SER blocked ADIW YL, Width LD temp, Y SBIW YL, Width ; restore Y AND temp, mask3 BRNE _checkdownside_end CLR blocked _checkdownside_end: RET checkleftside: SER blocked MOV templlo, mask3 LSR templlo BRNE _checkleftside_adjusted ROR templlo SBIW YL, 1 _checkleftside_adjusted: LD temp, Y AND temp, templlo BRNE _checkleftside_restore CLR blocked _checkleftside_restore: LSL templlo BRNE _checkleftside_end ADIW YL, 1 _checkleftside_end: RET checkrightside: SER blocked MOV templlo, mask3 LSL templlo BRNE _checkrightside_adjusted ROL templlo ADIW YL, 1 _checkrightside_adjusted: LD temp, Y AND temp, templlo BRNE _checkrightside_restore CLR blocked _checkrightside_restore: LSR templlo BRNE _checkrightside_end SBIW YL, 1 _checkrightside_end: RET ; ###################################################################### ; # ; # Routine 'xy_to_pointermask' ; # ; # Translates (x,y) pixel coordinate to a pointer to the byte ; # containing the pixel and the bitmask for the pixel in the byte. ; # ; # Parameters ; # ---------- ; # (n) temp_x (lo reg) = x coordinate ; # ; # (n) temp_y (lo reg) = y coordinate ; # ; # (c) cntr (hi reg) = temporary counter ; # ; # (c) temp (hi reg) = temporary register ; # ; # (c) byteoff (lo reg) = temporary register ; # ; # (d) offsetlo (lo reg) = lower byte of pointer ; # ; # (d) offsethi (lo reg) = upper byte of pointer ; # ; # (d) mask3 (lo reg) = pixel's bitmask ; # ; ###################################################################### xy_to_pointermask: ; divide x by 8 to get horizontal byte offset MOV offsetlo, temp_x LSR offsetlo LSR offsetlo LSR offsetlo MOV byteoff, offsetlo ; multiply by 22 (Width) to get vertical byte offset CLR temp MOV offsetlo, temp_y CLR offsethi LSL offsetlo ROL offsethi LSL offsetlo ROL offsethi ADD offsetlo, temp_y ADC offsethi, temp LSL offsetlo ROL offsethi ADD offsetlo, temp_y ADC offsethi, temp LSL offsetlo ROL offsethi ; offsethi:offsetlo points to byte containing (x,y) ADD offsetlo, byteoff ADC offsethi, temp LDI temp, 0b00000001 MOV mask3, temp MOV cntr, temp_x ANDI cntr, 0b00000111 ; the ANDI did an implicit compare with zero BREQ _xy_to_pointermask_shiftdone _xy_to_pointermask_shift: ; single bit mask useful for horizontal wall detection LSL mask3 DEC cntr BRNE _xy_to_pointermask_shift _xy_to_pointermask_shiftdone: RET ; ###################################################################### ; # ; # Procedure 'killpillvert' ; # ; # Kills a pill in both the pill array when Pac-Man ; # approaches the pill vertically. ; # ; # Parameters ; # ---------- ; # (n) mask3 (gen reg) = mask for position pixel in the byte ; # ; # (n) Z (gen word reg) = pointer to byte in pill array ; # ; # (c) pillsleft (RAM var) = number of pills remaining ; # ; ###################################################################### killpillvert: COM mask3 ; now the mask will mask out the common bit LD temp, Z ; DON'T FORGET TO RELOAD TEMP!!!!! AND temp, mask3 SEC ROR mask3 BRCS _killpillvert_updatemask1 ST Z, temp ROR mask3 LD temp, -Z _killpillvert_updatemask1: AND temp, mask3 ST Z, temp CLR temp0 SBIW ZL, Width ; go one line up SBRS killpilldir, 7 ; if we are colliding from above ADIW ZL, 2 * Width ; go two lines down LD temp, Z AND temp, mask3 SEC ROL mask3 BRCS _killpillvert_updatemask2 ST Z+, temp ROL mask3 LD temp, Z _killpillvert_updatemask2: AND temp, mask3 ST Z, temp ; pill killed LDI XL, low(pillsleft) LDI XH, high(pillsleft) LD horzPT, X+ LD vertPT, X SUBI horzPT, low(1) SBCI vertPT, high(1) ST X, vertPT ST -X, horzPT RET ; ###################################################################### ; # ; # Procedure 'killpillhorz' ; # ; # Kills a pill in both the pill array when Pac-Man ; # approaches the pill horizontally. ; # ; # Parameters ; # ---------- ; # (n) mask3 (gen reg) = mask for position pixel in the byte ; # ; # (n) Z (gen word reg) = pointer to byte in pill array ; # ; # (c) pillsleft (RAM var) = number of pills remaining ; # ; ###################################################################### killpillhorz: COM mask3 ; now the mask will mask out the common bit CPI killpilldir, 1 BRNE _killpillhorz_initdone SEC ROL mask3 BRCS _killpillhorz_initdone ADIW ZL, 1 ROL mask3 _killpillhorz_initdone: LD temp, Z ; DON'T FORGET TO RELOAD TEMP!!!!! AND temp, mask3 SEC ROR mask3 BRCS _killpillhorz_updatemask1 ST Z, temp SBIW ZL, 1 ROR mask3 LD temp, Z _killpillhorz_updatemask1: AND temp, mask3 ST Z, temp ADIW ZL, Width ; go to the next line of the pill LD temp, Z AND temp, mask3 SEC ROL mask3 BRCS _killpillhorz_updatemask2 ST Z, temp ADIW ZL, 1 ROL mask3 LD temp, Z _killpillhorz_updatemask2: AND temp, mask3 ST Z, temp ; pill killed LDI XL, low(pillsleft) LDI XH, high(pillsleft) LD horzPT, X+ LD vertPT, X SUBI horzPT, low(1) SBCI vertPT, high(1) ST X, vertPT ST -X, horzPT RET