; i'm so sorry for this file PUSHS "Graphics Variables", WRAM0[PRINTER_VARS_START+$80] vCurrentLineOAMOffsets: ds 16 ; 10 bytes which each store an offset into MY_OAM vCurrentScanline: db ; what scanline are we looknig at vCurrentDotX: db ; what x position we're looknig at vCurrentPixelColor: db ; the pixel we're looking at's color value, 2 bits vTopHalfOfSprite: ds $20 ; two tiles of tile data for the top half of a sprite vBottomHalfOfSprite: ds $20 ; two tiles of tile data for the bottom half of a sprite POPS BuildRelevantSpritesList: ld a, 0 ; a should now hold how many sprites we've looked at for this row push af ld de, vCurrentLineOAMOffsets ld a, $ff ld b, 14 ; populatte oam offsets with $ff, an indicator that no sprite is found .buildBlankList ld [de], a inc de dec b jp nz, .buildBlankList ld de, vCurrentLineOAMOffsets ld bc, $0000 ; starting offset .loop pop af ; counter for how many sprites we've added on thiis row call IsThisSpriteRelevant call c, AddThisSpriteToDE ; if it is relevant tthen add it push af ; save the counter for the next row cp a, 10 ; if the new counter is 10, then we're full up! and done w sprites jp z, .doneWithOAM inc c inc c inc c inc c ld a, c cp a, $A0 jp z, .doneWithOAM jp .loop .doneWithOAM pop af ld a, [vCurrentScanline] ret IsThisSpriteRelevant: push af ; don't wanna squash a ; bc is an offset into an OAM ; set the carry flag if we have to draw the sprite at OAM+bc on ths line. ld hl, MY_OAM ld a, [vCurrentScanline] add hl, bc ; get OAM record. first byte is y value sub a, [hl] ; vCurrentScanline - yvalue jp c, .irrelevant ; if it carries, then the sprite is strrictly greater ; than scanline. not relevant. cp a, 8 jp nc, .irrelevant ; if it doesn't carry, then a-[hl] is greater than or ; equal to 8. not relevant. .relevant ; if it doesn't satisfy either of those circumstances, then it's relevant. pop af scf ; set carry ret .irrelevant pop af scf ccf ; clear carry = set carry + complement carry ret AddThisSpriteToDE: inc a ; increment a to signal we've added a sprite to the relevant sprites push af ld a, c ; bc holds hte offset iinto OAM, which always sstarts with $00 ld [de], a inc de pop af ret AddSpritesToLine: ; operates on sscanline [vCurrentScanline] and buffer BUFFER_ONE push af push bc call BuildRelevantSpritesList ld a, 16 ; account for border ld [vCurrentDotX], a .loopOverX call GetCurrentBackgroundPixelValueFromBuffer ld hl, vCurrentLineOAMOffsets .loopOverSprites ld b, 0 ld c, [hl] bit 0, c jp nz, .doneLoopingOverSprites ; if we find an offset with the first bit set ; then it has to be an endd-of-list flag so we're done looping over sprites. ; otherrwise we have a valid sprite offset in bc push hl ; push hl (position in vCurrentLineOAMOffsees) to save for later ; this is popped in .nextSprite ld hl, MY_OAM add hl, bc ; index into MY_OAM inc hl ; we're already certain it's in the range for y, so look at x ld a, [vCurrentDotX] ; current x value sub a, [hl] ; dx = x - sx -> if it carries, we skip jp c, .nextSprite cp a, 8 ; dx - 8 -> if it doesn't carry, we skip jp nc, .nextSprite dec hl ; look at whole oam record ;now we've ensured that 0 <= dx < 8 ;get pixel A from sprite in oam ; a holds dx ; hl holds an OAM record call GetPixelFromOAM ; a now holds a color id. if thatt color id is zero, go to the next sprite ; hl points to the OAM record cp a, 0 jp z, .nextSprite push af ; hold on to color id. inc hl ; point to x value inc hl ; point tto tile id inc hl ; point to attributes ; palette is bit 4 [hl] ; priority is bit 7 [hl] bit OAMB_PRI, [hl] ; if priority is zero, continue writing sprite color (skip this block) jp z, :+ ld a, [vCurrentPixelColor] ; else priority iis 1 cp a, 0 jp z, :+ ; if priority is 1 and background is zero, continue writing sprite color pop af ; gotta get that off the stack so it's clean jp .doneLoopingOverSprites ; if priiority is 1 AND backgruond is nonzero, ; we're done with this pixel forever. this does not properly deal with priority ; but it should be good enough for thiis program. : pop af cp a, 0 jp z, .nextSprite ; if the color index of the sprite is zero, then we can skip push af ; get color from palette ld a, [rOBP0] bit OAMB_PAL1, [hl] jp z, :+ ld a, [rOBP1] : ld b, a ; put the palette into b pop af ; right shift the palette to get the specific color ; index (a) guaranteed to not be zero bc we checked for that above : srl b srl b dec a ; shift right twice per color index jp nz, :- ld a, b and a, $3 ; now holds just the color itself call WritePixelToBuffer .nextSprite pop hl inc hl jp .loopOverSprites .doneLoopingOverSprites ld hl, vCurrentDotX inc [hl] ld a, 80 cp a, [hl] jp nc, .loopOverX pop bc pop af ret GetPixelFromOAM: ; hl takes an oam record ; a has an x offset ; bit 5, [hl+3] = x flip ; de takes the yx offsets push hl push de ld e, a ; hold on to x offset ld a, [vCurrentScanline] ; y position sub a, [hl] ; currentScanline - y position ld d, a ; hold on to y offset inc hl ; look at x coord inc hl ; look at tile id ld a, [hl] call FindCardSpriteTiles ; puts card sprite tiles in hl call FindTileData ; uses a + hl to get tile data ld a, d ; y offset is number of pairs of bytes to skip cp a, 0 jp z, .doneFindingYLine .loopFindYLine inc hl inc hl dec a jp nz, .loopFindYLine .doneFindingYLine ;now hl points to two bytes which correspond to the correct tile data. ; abcd_efghi jklm_nopq ld b, [hl] ; first bitplane inc hl ld c, [hl] ; second bitplane ld a, 7 sub a, e ; number of times to shift right to put the bit we want in positoin 0 cp a, 0 jp z, .doneShiftingBC .shiftbc srl b srl c dec a jp nz, .shiftbc .doneShiftingBC ld a, b and a, $1 ld b, a sla c ld a, c and a, $2 or a, b ; now a holds the color id!! pop de pop hl ret GetCurrentBackgroundPixelValueFromBuffer: ; takes arguments from vCurrentScanline and vCurrentDotX, gets the two-bit pixel ; value from the tile data llinearly packed at BUFFER_ONE call FindCurrentBufferBytes ; sets hl and bc. b = msb tile, c = lsb tile ld a, [vCurrentDotX] and a, $7 ld e, a ; e = x % 8 = xoff ; now extract the specific pixel ld a, 7 sub a, e ; 7-xoff = number of times to shift to the right cp a, 0 jp z, .doneBitShifting .bitShiftLoop srl b srl c dec a jp nz, .bitShiftLoop .doneBitShifting ld a, b and a, $1 sla a ld b, a ld a, c and a, $1 or a, b ld [vCurrentPixelColor], a ret WritePixelToBuffer: ; takes 2-bit color as a, yx offsets as de. ; overwrites the pixel in the buffer with the color passed in. ld b, a ; hold on to the color we're trying to write and a, $1 ; LSB first ld c, a ld a, b sra a ; MSB next ld b, a ; now b and c hold msb and lsb ld a, 7 sub a, e ; get x offset from right side (7-xoff) jp z, :++ : sla b sla c dec a jp nz, :- : ; now b and c have been shifted left according to their values. push bc call FindCurrentBufferBytes call WipeTargetBit pop bc ld a, c or a, [hl] ld [hl], a ; lsb first inc hl ld a, b or a, [hl] ld [hl], a ret WipeTargetBit: ; hl points to a row, e holds an xoff ; sets the xoff bit of [hl] and [hl+1] to zero ; gonna make a mask that has 1s everywhere except for a zero at bit xoff push de ld d, $7F ; shift right by e bits ld a, 0 cp a, e ld a, d jp z, :++ : srl a or a, $80 dec e jp nz, :- : ; apply the mask witth AND to [hl] and [hl+1] ld d, a ;ld a, d and a, [hl] ld [hl], a inc hl ld a, d and a, [hl] ld [hl], a dec hl pop de ret FindCurrentBufferBytes: ; points hl at the bytes of BUFFER_TWO where the current scanline/dot live ld a, [vCurrentScanline] sub a, 16 ; subtract off the overscan and a, $7 ; scanline mod 8, so we can step through the buffer ld d, a ; store y offset within this row of tiles ld a, [vCurrentDotX] sub a, 8 ; subtract off the overscan and a, $7 ; current dot mod 8, for an offste ld e, a ld a, [vCurrentDotX] sub a, 8 ; subtract off the border sra a sra a sra a ; divide a by 8 to geet numbre of tiles we want to step past ; now we want to get hl to point to the two bytes which define the pixel we want. ld h, 0 ld l, a ; numbeer of tiles we want to step past. 16 bytes per this add hl, hl ; number of tiles times 2 add hl, hl ; times 4 add hl, hl ; times 8 add hl, hl ; tmies 16 si the number of bytes to step forward ld bc, BUFFER_TWO add hl, bc ; this is pointing at the start of a tile. ; now step down by 2*d bytes to get the row we want ld b, 0 ld c, d ; this is the y offset ; add twice y offset to hl to get the row we want. add hl, bc add hl, bc ; now we're pointing at the two bytes we want to extract a pixel from. ld c, [hl] ; LSB fiirst inc hl ld b, [hl] ; MSB second dec hl ret