; ; egfx_core.asm -- graphics functions for Egfx ; ; This file is included by "egfx.asm". ; See egfx.inc for all the neat things that can be done. ; ; variable notes: ; egfx_column is current mux column times two ; egfx_numcolors is number of colors (including off) minus one ; egfx_baseline is the baseline value (off value) minus one ; egfx_spt is the scanline pointer table, which is 60 bytes long. ; ; Script functions ; ; ScriptResource calls another script ; resource ID in b. Egfx_ScriptResource subroutine jsr Param_B ScriptResource pshx jsr Resource_GetResource ; get resource pointer bcs .bad ; if bad, ignore it jsr ExecuteScript ; otherwise, do the script .bad pulx rts ; ; Buffer functions ; ; Wipe erases the entire video buffer with the baseline color. Egfx_Wipe subroutine Wipe psha pshb pshx ldd egfx_bufend ; get pointer to end of buffer xgdx ; put it in x ldaa egfx_baseline ; get the baseline - 1 inca ; make it the baseline value .wipeloop dex ; decrement pointer staa 0,x ; store it cpx egfx_bufferptr ; are we done? bne .wipeloop ; if not, keep wiping. pulx pulb pula rts ; ; Drawing functions ; ; Dot draws a pixel at the cursor location using the current drawing mode ; and settings. If automove is set, it also adjusts the cursor location. Egfx_Dot subroutine Dot psha pshb pshx ldx egfx_cursorptr ; get a pointer to the cursor jsr CurrentDmodeVec ; draw the pixel jsr AutoMoveVec ; do the automove stx egfx_cursorptr ; store the new cursor pointer pulx pulb pula rts ; Skip draws nothing, but does the automove if it is set. Egfx_Skip subroutine Skip psha pshb pshx ldx egfx_cursorptr ; get a pointer to the cursor jsr AutoMoveVec ; do the automove stx egfx_cursorptr ; store the new cursor pointer pulx pulb pula rts ; Xline draws a horizontal line using the current mode. ; If width is positive, the line is drawn width pixels to the right ; of the cursor; otherwise, it is drawn width pixels to the left. ; The cursor is moved to the end of the line, and then the automove, ; if any, is performed. ; width is in a. Egfx_Xline subroutine jsr Param_A Xline psha pshb pshx ldx egfx_cursorptr ; get cursor pointer tsta ; is a plus or minus? bmi .dominus ; if negative, draw to the left beq .done ; if zero, draw nothing .rightloop jsr CurrentDmodeVec ; draw the pixel deca ; decrement counter beq .done ; if done, go away jsr MoveRight ; move to the right bra .rightloop ; do some more .dominus .leftloop jsr CurrentDmodeVec ; draw the pixel inca ; increment negative counter beq .done ; if done, we're done jsr MoveLeft ; move to the left bra .leftloop ; do some more .done jsr AutoMoveVec ; do the automove stx egfx_cursorptr ; store the new cursor pointer pulx pulb pula rts ; Yline draws a vertical line using the current mode. ; It is just like Xline except vertical. ; height is in b. Egfx_Yline subroutine jsr Param_B Yline psha pshb pshx tba ; put thingee in a ldx egfx_cursorptr ; get cursor pointer tsta ; is a plus or minus? bmi .dominus ; if negative, draw to up beq .done ; if zero, draw nothing .downloop jsr CurrentDmodeVec ; draw the pixel deca ; decrement counter beq .done ; if done, stop jsr MoveDown ; otherwise move down bra .downloop ; and keep going .dominus .uploop jsr CurrentDmodeVec ; draw the pixel inca ; increment negative counter beq .done ; if done, stop jsr MoveUp ; otherwise move up bra .uploop ; and keep going .done jsr AutoMoveVec ; do the automove stx egfx_cursorptr ; store the new cursor pointer pulx pulb pula rts ; Frame draws a rectangular frame using the current mode. ; The cursor is left in the same place, except for the automove. ; width is in a, height is in b. Egfx_Frame subroutine jsr Param_AB Frame psha pshb psha ; stash width on stack ldaa #OPCODE_RTS ; temporarily disable automove staa AutoMoveVec pula jsr Xline ; draw the first horizontal line jsr Yline ; draw the first vertical line nega ; negate width jsr Xline ; draw the second horizontal line ldaa #OPCODE_JMPEXT ; enable automove staa AutoMoveVec negb ; negate height jsr Yline ; draw the second vertical line pulb pula rts ; Rect draws a filled rectangle using the current mode. ; The cursor is left in the same place, except for the automove. ; width is in a, height is in b. Egfx_Rect subroutine jsr Param_AB Rect psha pshb pshx psha ; stash a ldaa #OPCODE_RTS ; disable autmove staa AutoMoveVec pula ; restore a ldx egfx_cursorptr ; get the current cursor pointer tsta ; is width negative or positive? bmi .dominus ; if negative, count backwards beq .done ; it zero, do nothing. .rightloop psha ; stash a ldaa egfx_ycursor ; get cursor pos psha ; stash cursor position on stack jsr Yline ; draw a vert line of the correct height pula ; get back cursor pos staa egfx_ycursor ; store it pula ; get back a pshb ; save b jsr MoveRight ; move a pixel to the right pulb ; restore b stx egfx_cursorptr ; store the new cursor pointer deca ; decrement width counter bne .rightloop ; if not done, do some more bra .done ; that's all .dominus .leftloop psha ; stash a ldaa egfx_ycursor ; get cursor pos psha ; stash cursor position on stack jsr Yline ; draw a vert line of the correct height pula ; get back cursor pos staa egfx_ycursor ; store it pula ; get back a pshb ; save b jsr MoveLeft ; move a pixel to the right pulb ; restore b stx egfx_cursorptr ; store the new cursor pointer inca ; increment negative counter bne .leftloop ; if not done, do some more .done ldaa #OPCODE_JMPEXT ; enable automove staa AutoMoveVec jsr AutoMoveVec ; do the automove stx egfx_cursorptr ; store the new cursor pointer pulx pulb pula rts ; ; screen functions ; ; ScreenSet adjusts the scanline pointer table so that the top-left corner ; of the visible screen is at the given coordinates in the video buffer. ; If the screen goes off the buffer in the horizontal direction, it ; wraps around. In the vertical direction, it does NOT wrap, and will ; be garbage. ; x is in a, y is in b Egfx_ScreenSet subroutine jsr Param_AB ScreenSet psha pshb pshx pshy pshb ; stash y value on stack ldab egfx_ysize ; get buffer height mul ; multiply by x value xgdx ; put result in x register pulb ; get back y value abx ; add to result xgdx ; put result in d register addd egfx_bufferptr ; add pointer to start of buffer ldx #0 ; start with scanline 0 ldy #egfx_hpt ; put in the header pointers too .loop pshx ldx egfx_headerptr ; get the header pointer stx 0,y ; store it in the table iny ; next entry iny pulx .wrap cpd egfx_bufend ; is it past the end of the buffer? blo .nowrap ; if not, no need to wrap subd egfx_bufsize ; if so, subtract a buffer length bra .wrap ; and try again .nowrap std egfx_spt,x ; store the pointer in the scanline table addd egfx_ysize16 ; go to next line in buffer inx ; go to next scanline pointer inx cpx #58 ; is it done yet? bls .loop ; if not, keep going. puly pulx pulb pula rts ; RangeSet sets a range of scanlines to the given coordinates. ; x is in a, y is in b, starting column number is in x, width is in y Egfx_RangeSet subroutine jsr Param_XYAB RangeSet psha pshb pshx pshy xgdx ; put x into d aslb ; multiply column number by two xgdx ; put back into x pshx ; stash on stack for later pshb ; stash y value on stack ldab egfx_ysize ; get buffer height mul ; multiply by x value xgdx ; put result in x register pulb ; get back y value abx ; add to result xgdx ; put result in d register addd egfx_bufferptr ; add pointer to start of buffer pulx ; get back column number thing .loop cpd egfx_bufend ; is it past the end of the buffer? blo .nowrap ; if not, no need to wrap subd egfx_bufsize ; if so, subtract a buffer length bra .loop ; and try again .nowrap std egfx_spt,x ; store the pointer in the scanline table addd egfx_ysize16 ; go to next line in buffer inx ; go to next scanline pointer inx dey ; decrement width counter bne .loop ; if not zero yet, keep going. puly ; get back counter in y pshy xgdx ; put x (last column plus one times two) in d addd #egfx_hpt-2 ; add offset to header ptr table minus two xgdx ; put ptr to last header ptr back in x ldd egfx_headerptr ; get the header ptr .hptloop std 0,x ; store it dex ; go to previous one dex dey ; decrement counter bne .hptloop ; if not zero, keep going puly pulx pulb pula rts ; Pan moves the entire screen within the video buffer, according to the ; current pan setting Egfx_Pan subroutine Pan psha pshb pshx pshy ldy #58 ; go to the last scanline ldx #egfx_hpt+58 ; go to the last entry in the header ptr table .loop pshx ; store the table ptr ldx 0,x ; put the header ptr in x jsr PanVec ; do the pan function pulx ; get back table ptr dex ; go to previous table entry dex dey ; go to previous scanline dey bne .loop ; if not done, loop ldx 0,x jsr PanVec ; do the last one puly pulx pulb pula rts ; PanThis pans the entire screen according to the given direction. ; Direction is in a. Egfx_PanThis subroutine jsr Param_A PanThis pshx ldx PanVec+1 ; get the old pan vector pshx ; stash it jsr SetPan ; set a new pan mode jsr Pan ; do the pan pulx ; get back the old pan mode stx PanVec+1 ; restore it pulx rts ; very cheesy implementation, yes... ; PanRange pans a range of scanlines according to the current pan setting. ; starting column is in a, range width is in b Egfx_PanRange subroutine jsr Param_AB PanRange psha pshb pshx pshy stab egfx_stash1 ; store width in stash1 tab ; put column in b clra ; clear hibyte aslb ; multiply column by two ldx #egfx_hpt ; get table pointer abx ; add column times two xgdy ; put column times two into y .loop pshx ; store the table ptr ldx 0,x ; put the header ptr in x jsr PanVec ; do the pan function pulx ; get back table ptr iny ; go to next scanline ptr iny inx ; go to next table entry inx dec egfx_stash1 ; decrement width counter bne .loop ; if not done, loop puly pulx pulb pula rts ; ; cursor functions ; ; CursorSet moves the cursor to the given absolute coordinates. ; If the values are outside the valid range, they wrap. ; x is in a, y is in b. Egfx_CursorSet subroutine jsr Param_AB CursorSet psha pshb pshx .xloop cmpa egfx_xsize ; is x too big? blo .yloop ; if not, try y suba egfx_xsize ; if so, wrap around bra .xloop ; keep at it until it's valid .yloop cmpb egfx_ysize ; if y too big? blo .wrapdone ; if not, we're okay subb egfx_ysize ; if so, wrap around bra .yloop ; keep at it until it's valid .wrapdone staa egfx_xcursor ; store the new x value stab egfx_ycursor ; store the new y value pshb ; stash y value on stack ldab egfx_ysize ; get buffer height mul ; multiply by x value xgdx ; put result in x register pulb ; get back y value abx ; add to result xgdx ; put result in d register addd egfx_bufferptr ; add pointer to start of buffer std egfx_cursorptr ; store it as the cursor pointer pulx pulb pula rts ; Xset sets the x value of the cursor. ; x is in a Egfx_Xset subroutine jsr Param_A Xset pshb ldab egfx_ycursor ; we have to do the multiply anyway jsr CursorSet ; so might as well do it the easy way pulb rts ; Yset sets the y value of the cursor. ; y is in b Egfx_Yset subroutine jsr Param_B Yset psha pshb .yloop cmpb egfx_ysize ; is it too big? blo .wrapdone ; if not, we're okay subb egfx_ysize ; if so, wrap around bra .yloop ; keep going until it works .wrapdone clra ; put zero in high byte subb egfx_ycursor ; sub current y value from new y value bpl .itsplus ; if it's positive, don't worry about it deca ; if it's negative, make high byte $FF .itsplus addd egfx_cursorptr ; add 16-bit signed value to cursor ptr std egfx_cursorptr ; store it pulb stab egfx_ycursor ; store the new y cursor pula rts ; Xjump moves the cursor horizontally by the specified amount. ; If it goes too far, it wraps. ; delta x is in a. Egfx_Xjump subroutine jsr Param_A Xjump psha tsta bmi .dominus adda egfx_xcursor ; add to current cursor position .plusloop cmpa egfx_xsize ; is it off the screen? blo .isgood ; if not, use it suba egfx_xsize ; otherwise, do some wrapping bra .plusloop ; and keep trying. .dominus adda egfx_xcursor ; add to current cursor position bcs .isgood ; if it didn't go off the screen, it's good .minusloop adda egfx_xsize ; if it did, do a wrap bcc .dominus ; if that still didn't do it, do it again .isgood jsr Xset ; go to this x position pula rts ; Yjump moves the cursor vertically by the specified amount. ; If it goes too far, it wraps. ; delta y is in b. Egfx_Yjump subroutine jsr Param_B Yjump pshb tstb bmi .dominus addb egfx_ycursor ; add to current cursor position .plusloop cmpb egfx_ysize ; is it off the screen? blo .isgood ; if not, use it subb egfx_ysize ; otherwise, do some wrapping bra .plusloop ; and keep trying. .dominus addb egfx_ycursor ; add to current cursor position bcs .isgood ; if it didn't go off the screen, it's good .minusloop addb egfx_ysize ; if it did, do a wrap bcc .dominus ; if that still didn't do it, do it again .isgood jsr Yset ; go to this y position pulb rts ; CursorStore stores the current cursor position in reg 0 for later retrieval. Egfx_CursorStore subroutine CursorStore psha pshx ldx egfx_headerptr ; get buf header ptr ldaa egfx_xcursor ; get the x position staa HEADER_CursorX0,x ; store it ldaa egfx_ycursor ; get the y position staa HEADER_CursorY0,x ; store it pulx pula rts ; CursorStoreNum stores the cursor position in one of the 4 registers. ; register num in b Egfx_CursorStoreNum subroutine jsr Param_B CursorStoreNum pshb pshx ldx egfx_headerptr ; get buf header abx ; add offset for register num ldab egfx_xcursor ; get the x position stab HEADER_CursorX0,x ; store it ldab egfx_ycursor ; get the y position stab HEADER_CursorY0,x ; store it pulb pulx rts ; CursorRestore restores the cursor from register 0. Egfx_CursorRestore subroutine CursorRestore psha pshb pshx ldx egfx_headerptr ; get buffer header ptr ldaa HEADER_CursorX0,x ; get x position ldab HEADER_CursorY0,x ; get y position jsr CursorSet ; go there pulx pulb pula rts ; CursorRestoreNum restores the cursor from one of the registers. ; register num in b Egfx_CursorRestoreNum subroutine jsr Param_B CursorRestoreNum psha pshb pshx ldx egfx_headerptr ; get buffer header ptr abx ; add offset for register num ldaa HEADER_CursorX0,x ; get x position ldab HEADER_CursorY0,x ; get y position jsr CursorSet ; go there pulx pulb pula rts ; ; setting functions ; ; SetBuffer sets the current drawing buffer to the given resource. ; resource ID is in b. Egfx_SetBuffer subroutine jsr Param_B SetBuffer jsr Resource_GetResource ; get the resource ptr bcs .bad ; if bad resource id, do nothing stx egfx_headerptr ldaa HEADER_xsize,x ; otherwise, transfer data staa egfx_xsize ldaa HEADER_ysize,x staa egfx_ysize ldd HEADER_bufferptr,x std egfx_bufferptr ldd HEADER_bufsize,x std egfx_bufsize ldd HEADER_bufend,x std egfx_bufend ldaa egfx_xcursor ; set the cursor position in this buf ldab egfx_ycursor ; (wrapping if necessary) jsr CursorSet .bad rts ; SetColor sets the current color value. ; color is in a. Egfx_SetColor subroutine jsr Param_A SetColor staa egfx_color ; store it rts ; that's all ; SetDelta sets the current delta value. ; delta is in a. Egfx_SetDelta subroutine jsr Param_A SetDelta staa egfx_delta ; store it rts ; that's all ; SetBaseline sets the current baseline value. ; baseline is in a. Egfx_SetBaseline subroutine jsr Param_A SetBaseline deca ; egfx_baseline is baseline - 1 staa egfx_baseline ; store it inca ; restore entry value rts ; that's all ; SetDrawingMode sets the drawing mode for subsequent drawing operations. ; drawing mode code is in a. Egfx_SetDrawingMode subroutine jsr Param_A SetDrawingMode psha pshb pshx cmpa #EGFX_NUMDMODES ; is the number invalid? blo .allgood ; if not, continue clra ; if so, set the nothing mode .allgood tab ; put code in b aslb ; multiply by two ldx #DmodeTable ; get ready for a cheesy lookup table abx ; add offset ldd 0,x ; get pointer to function std CurrentDmodeVec+1 ; store it in the vector pulx pulb pula rts ; SetAutoMove sets the automove function for subsequent drawing operations. ; If automove >= AUTOMOVE_SCRIPT, then the graphics script with resource ; ID autmove-AUTOMOVE_SCRIPT is executed. ; automove code in is a. Egfx_SetAutoMove subroutine jsr Param_A SetAutoMove psha pshb pshx cmpa #AUTOMOVE_SCRIPT ; is it a real automove? blo .allgood ; if so, continue suba #AUTOMOVE_SCRIPT ; if not, it's a script! Get the ID. staa egfx_automovescript ; store it ldd #MoveScript ; get pointer to automove function bra .storeit ; go store it in the vector .allgood tab ; put code in b aslb ; multiply by two ldx #AutoMoveTable ; get ready for a cheesy lookup table abx ; add offset ldd 0,x ; get pointer to function .storeit std AutoMoveVec+1 ; store it in the vector pulx pulb pula rts ; SetPan sets the pan function for subsequent pan operations. ; pan code in is a. Egfx_SetPan subroutine jsr Param_A SetPan psha pshb pshx cmpa #EGFX_NUMPANS ; is the number invalid? blo .allgood ; if not, continue clra ; if so, set the nothing mode .allgood tab ; put code in b aslb ; multiply by two ldx #PanTable ; get ready for a cheesy lookup table abx ; add offset ldd 0,x ; get pointer to function .storeit std PanVec+1 ; store it in the vector pulx pulb pula rts ; ; private functions ; ; DmodeTable is a lookup table for the mode functions DmodeTable dc.w DmodeNone dc.w DmodeCopy dc.w DmodeBlend dc.w DmodeShade dc.w DmodeInvert dc.w DmodeErase ; The DMode functions assume that the cursor position is in x, ; and they destroy the b register. ; DmodeCopy copies the color variable to the cursor location. DmodeCopy subroutine ldab egfx_color ; get the color value stab 0,x ; store it rts ; DmodeBlend averages the color variable and the value already at ; the cursor position. DmodeBlend subroutine ldab 0,x ; get the current pixel value addb egfx_color ; add the color value rorb ; divide by two, accounting for carry out stab 0,x ; store it rts ; DmodeShade adds the delta value to the current pixel value. ; It clips at 0 or 255, depending on the sign of delta. DmodeShade subroutine ldab 0,x ; get the current pixel value addb egfx_delta ; add the delta value bcc .noclip ; if no carry out, everything's fine ldab egfx_delta ; get the sign of delta bmi .darken ; are we darkening? ldab #$FF ; if not, clip to 255 bra .noclip .darken clrb ; if so, clip to 0 .noclip stab 0,x ; store the new value rts ; DmodeInvert replaces the pixel with: ; maxcolor - (pixel - baseline) + baseline. ; If the pixel is between baseline and baseline+maxcolor, ; it appears to invert the brightness. DmodeInvert subroutine ldab egfx_numcolors ; maxcolor + subb 0,x ; - pixel addb egfx_baseline ; + (baseline - 1) addb egfx_baseline ; + (baseline - 1) addb #2 ; + 2 stab 0,x ; store it rts ; DmodeErase copies the baseline to the cursor location DmodeErase subroutine ldab egfx_baseline ; get baseline - 1 incb ; adjust for real baseline stab 0,x ; store it rts ; DmodeNone does nothing. Eet daus nauthing. DmodeNone subroutine rts ; AutoMoveTable is a lookup table for the automove functions AutoMoveTable dc.w MoveNone dc.w MoveLeft dc.w MoveRight dc.w MoveUp dc.w MoveDown ; The Move functions assume the cursor position is in x. ; They return the new cursor position in x. ; They destroy the b register. ; MoveNone does not move the cursor MoveNone subroutine rts ; MoveLeft moves the cursor one pixel to the left MoveLeft subroutine dec egfx_xcursor ; decrement xcursor xgdx ; put cursor pos in d subd egfx_ysize16 ; subtract the buffer height cpd egfx_bufferptr ; did it wrap? bhs .nowrap ; if not, it's all good addd egfx_bufsize ; if so, wrap to the right side xgdx ; put cursor pos back in x ldab egfx_xsize ; get buffer width decb ; minus one stab egfx_xcursor ; store it as xcursor rts .nowrap xgdx ; put cursor pos back in x rts ; MoveRight moves the cursor one pixel to the right MoveRight subroutine inc egfx_xcursor ; increment xcursor ldab egfx_ysize ; get buffer height abx ; add to cursor position cpx egfx_bufend ; is it too big now? blo .nowrap ; if not, it's all good xgdx ; if so, put x into d subd egfx_bufsize ; wrap to the left side xgdx ; put it back clr egfx_xcursor ; set xcursor to zero .nowrap rts ; MoveUp moves the cursor one pixel up MoveUp subroutine dex ; decrement pointer dec egfx_ycursor ; decrement ycursor bpl .nowrap ; is that a wrap? ldab egfx_ysize ; if so, get ready to add ysize abx ; add it to x decb ; put ysize - 1 in b stab egfx_ycursor ; that's the new ycursor .nowrap rts ; MoveDown moves the cursor down one pixel MoveDown subroutine inx ; increment pointer ldab egfx_ycursor ; get the ycursor incb ; increment it cmpb egfx_ysize ; is it too big? blo .nowrap ; if not, no wrap xgdx ; if so, put x into d subd egfx_ysize16 ; subtract buffer height xgdx ; put it back clrb ; say that ycursor is zero .nowrap stab egfx_ycursor ; store ycursor rts ; MoveScript calls the graphics script in egfx_automovescript. MoveScript subroutine ldab egfx_automovescript ; get the resource ID jsr ScriptResource ; do it ldx egfx_cursorptr ; get the cursor pointer in x rts ; PanTable is a lookup table for the pan functions. PanTable dc.w PanNone dc.w PanLeft dc.w PanRight dc.w PanUp dc.w PanDown ; The Pan functions destroy d. Each one only pans one scanline. ; They assume the column number, times two, is in y. ; They assume a pointer to the buffer header is in x. ; PanNone does nothing PanNone subroutine rts ; PanLeft shifts the scanline pointer to the left, wrapping if necessary PanLeft subroutine ldd egfx_spt,y ; get the scanline pointer subd HEADER_ysize16,x ; go to the previous column cpd HEADER_bufferptr,x ; did it go too far? bhs .nowrap ; if not, continue addd HEADER_bufsize,x ; if so, wrap .nowrap std egfx_spt,y ; store the scanline pointer rts ; PanRight shifts the scanline pointer to the right, wrapping if necessary PanRight subroutine ldd egfx_spt,y ; get the scanline pointer addd HEADER_ysize16,x ; go to the next column cpd HEADER_bufend,x ; did it go too far? blo .nowrap ; if not, continue subd HEADER_bufsize,x ; if so, wrap .nowrap std egfx_spt,y ; store the scanline pointer rts ; PanUp shifts the scanline pointer up. No wrapping. Not a bit. PanUp subroutine ldd egfx_spt,y ; get the pointer subd #1 ; subtract one std egfx_spt,y ; put it back rts ; PanDown shifts the scanline pointer down. No wrapping here either. PanDown subroutine ldd egfx_spt,y ; get the pointer addd #1 ; add one std egfx_spt,y ; put it back rts ; RefreshHook handles muliplexing the display in enhanced mode. ; Effectively, a light is on for a fraction of its column's time slice equal to: ; (pixel value - baseline) / (number of colors - 1). ; If this fraction is less than 0 or greater than 1, it is clipped to 0 or 1 ; respectively. This interrupt hook is called VERY frequently, especially for ; high color depth, so it needs to be as fast as possible. For best results, ; the variables used should be in the direct page. The scanline pointer table ; (SPT) must be in the direct page. Note that egfx_baseline is actually the ; "baseline value" minus one. RefreshHook subroutine ldaa egfx_basecount ; get the color counter deca ; decrement by one bne .sameline ; if not zero, we're still on the same scanline ldab egfx_column ; get current column number (times 2) decb ; subtract two decb bpl .noscreenwrap ; if not neg, no need to wrap to the right side ldab #58 ; otherwise, get the rightmost column number .noscreenwrap stab egfx_column ; store the new column number staa PORT_Gfx_Data ; prevent shadowing by turning off the lights ; before moving stab PORT_Gfx_Column ; change current column (because of ; hardware flaw, times 2 is okay) xgdx ; put in x (a is already zero, after the bne) ldd egfx_spt,x ; get the scanline pointer from the table std egfx_scanline ; store it as the scanline ldaa egfx_numcolors ; set color counter to maximum .sameline staa egfx_basecount ; store new color count adda egfx_baseline ; add the baseline for comparison below ldx egfx_scanline ; get the scanline pointer ; start of scanline is in x ; current base value is in a (base goes from baseline+numcolors to baseline+1) ; output value is b cmpa 0,x ; if pixel > base, carry = 1 rorb ; rotate carry into output value cmpa 1,x rorb cmpa 2,x rorb cmpa 3,x rorb cmpa 4,x rorb cmpa 5,x rorb cmpa 6,x rorb ; because of hardware flaw, leave ; result in top 7 bits. stab PORT_Gfx_Data ; turn on the lights we want RefreshHookEnd _endhook RefreshHook