4-bit Grayscale Image Processor┌───────────────────────────────────────┐ │ ImageProcess4 │ │ Author: Phil Pilgrim │ │ (c) Copyright 2011 Bueno Systems, Inc.│ │ See end of file for terms of use. │ └───────────────────────────────────────┘ Revision History 2006.07.17: v0.1 Alpha release. 2014.01.16: v1.0 Initial release. Contact Information Phil Pilgrim: propeller@phipi.com IntroductionThis module provides drawing and image processing functions for 4-bit grayscale images, such as those obtained by the PropCAM (TM). It launches one additional cog for performing the image processing functions. The coordinate system used by this module assumes that (0,0) is the upper-leftmost point. X increases from left to right. Y increases from top to bottom. In any procedure call where either (x0, y0) or (x1, y1) lies outside the overall bounds of the display buffer, no action is taken and zero is returned.ConstantsSOURCE CODE... SETBUF = $00 ITERATE = $80 NO_OP = $30 WR1 = $02 WR0 = $01 Constants for User's Program
SOURCE CODE... LIN = $00 'Iterate over a straight line. RECT = $10 'Iterate over a rectangular area. BORDER = $20 'Iterate along the border of a "blob". EODIF = $08 'Even/odd differential: Pix(x,y) is ||(Pix(x, y & $fe) - Pix(x, y | 1)) FIND_ONE = $04 'Stop iterating when the first pixel meeting the mask condition is found. UNDEF = $8000_0000 'Undefined: Use for replacement values you don't want written. Instance VariablesSOURCE CODE... word width, height word stats[22] 'Image stats array byte started 'CogNo + 1 of started cog. Public Spin MethodsStart and Stop MethodsThis group of methods starts and stops the image processor.start(gray_buf_addr)Start this object and the cog associated with it.Parameters:
gray_buf_addr: the address of the 4-bit-deep grayscale buffer in which the work will be done.
The first word of this buffer contains the height in pixels.
The second word contains the width in pixels.
The remaining width * height / 2 bytes are the buffer area.
The buffer should be on a long boundary.
Return: true on success; false, otherwise.
Example: success := img.start(@gray_buf)
Start the object, pointing to an image buffer at gray_buf. Store result in success.
SOURCE CODE... PUB start(gray_buf_addr) stop if started := cognew(@entry, @_command) + 1 command(SETBUF << 24 | gray_buf_addr, 0, 0 ) width := word[gray_buf_addr][1] height := word[gray_buf_addr] return true else return false stopStop the object, and release its associated cog.Return: true if the object had been started; false, otherwise.
Example: img.stop
Stop the object.
SOURCE CODE... PUB stop if started cogstop(started - 1) started~ return true else return false Image Process MethodsThis group of methods analyzes areas within the image buffer and, optionally, modifies the pixels it encounters.box(px0, py0, px1, py1, pix1, pix0, condx)Draw a box (rectangle outline) with corners at (px0, py0) and (px1, py1). As the box is drawn, pixel values are replaced with pix1 or pix0, depending on whether its corresponding mask bit is a 1 or 0, respectively. If the replacement value is UNDEF, the pixel value is not replaced.Parameters:
px0: X coordinate of the box's first corner.
py0: Y coordinate of the box's first corner.
px1: X coordinate of the box's opposite corner.
py1: Y coordinate of the box's opposite corner.
pix1: The value to replace 1-masked pixels with, or UNDEF for no replacements.
pix0: The value to replace 0-masked pixels with, or UNDEF for no replacements.
condx: The 16-bit mask used to determine pixel type (high bit for 15; low bit for 0).
Return: The number of 1-masked pixels encountered.
Example: size := img.draw_box(50, 50, 80, 80, 15, img#UNDEF, %1111_1111_0000_0000)
Along the edge of the box bounded by (50, 50) and (80, 80) replace all pixels having values > 7 with 15.
Return the number of pixels whose values were > 7 and assign to size.
SOURCE CODE... PUB box(px0, py0, px1, py1, pix1, pix0, condx) : sum | p p := px0 <# px1 px1 #>= px0 <# width - 1 px0 := p #> 0 p := py0 <# py1 py1 #>= py0 <# height - 1 py0 := p #> 0 sum := line(px0, py0, px0, py1, pix1, pix0, condx, 0) if (px1 > px0) sum += line(px1, py0, px1, py1, pix1, pix0, condx, 0) if (px1 - px0 > 1) sum += line(px0 + 1, py0, px1 - 1, py0, pix1, pix0, condx, 0) if (py1 > py0) sum += line(px0 + 1, py1, px1 - 1, py1, pix1, pix0, condx, 0) crosshair(px, py, size, pix1, pix0, condx)Draw a crosshair centered on point (px, py) extending by size in each direction. When the crosshair is drawn, its pixel value is replaced with pix1 or pix0, depending on whether its corresponding mask bit is a 1 or 0, respectively. If the replacement value is UNDEF, the pixel value is not replaced.Parameters:
px: X-coordinate of crosshair.
py: Y-coordinate of crosshair.
size: How much to extend crosshair from center in each direction.
pix1: The value to replace 1-masked pixels with, or UNDEF for no replacements.
pix0: The value to replace 0-masked pixels with, or UNDEF for no replacements.
condx: The 16-bit mask used to determine pixel type (high bit for 15; low bit for 0).
Return: The number of 1-masked pixels encountered.
Example: img.draw_crosshair(64, 64, 2, 0, 15, %1111_1111_0000_0000)
Draw a crosshair at location (64, 64) 5 units high and wide (+2 in each direction).
Replace light-colored pixels (>7) with black (0); dark-colored pixels with white (15).
SOURCE CODE... PUB crosshair(px, py, size, pix1, pix0, condx) : sum ||size sum := line(px - size #> 0, py, px + size <# width - 1, py, pix1, pix0, condx, 0) if (size > 0) sum += line(px, py - size #> 0, px, py -1 <# height - 1, pix1, pix0, condx, 0) sum += line(px, py + 1 #> 0, px, py + size <# height - 1, pix1, pix0, condx, 0) point(px, py, pix1, pix0, condx)Draw a point at (px, py). When the point is drawn, its pixel value is replaced with pix1 or pix0, depending on whether its corresponding mask bit is a 1 or 0, respectively. If the replacement value is UNDEF, the pixel value is not replaced.Parameters:
px: X-coordinate of crosshair.
py: Y-coordinate of crsoohair.
pix1: The value to replace 1-masked pixels with, or UNDEF for no replacements.
pix0: The value to replace 0-masked pixels with, or UNDEF for no replacements.
condx: The 16-bit mask used to determine pixel type (high bit for 15; low bit for 0).
Return: The number of 1-masked pixels encountered.
Example: img.draw_point(45, 32, 7, img#UNDEF, $ffff)
Draw a point a location (45, 32) with a pixel value of 7, regardless of what
pixel value it replaces.
SOURCE CODE... PUB point(px, py, pix1, pix0, condx) return line(px, py, px, py, pix1, pix0, condx, 0) line(px0, py0, px1, py1, pix1, pix0, condx, stats_addr)Draw a line beginning at (px0, py0) and ending at (px1, py1), inclusive. As the line is drawn, pixel values are replaced with pix1 or pix0, depending on whether its corresponding mask bit is a 1 or 0, respectively. If the replacement value is UNDEF, the pixel value is not replaced.Parameters:
px0: X coordinate of the line's first end.
py0: Y coordinate of the line's first end.
px1: X coordinate of the line's opposite end.
py1: Y coordinate of the line's opposite end.
pix1: The value to replace 1-masked pixels with, or UNDEF for no replacements.
pix0: The value to replace 0-masked pixels with, or UNDEF for no replacements.
condx: The 16-bit mask used to determine pixel type (high bit for 15; low bit for 0).
stats_addr: Address of a 22-word word-aligned array, or zero. (See do below for an explanation.)
Return: The number of 1-masked pixels encountered.
Example: dark := img.line(0, 7, 127, 7, img#UNDEF, img#UNDEF, %0000_0000_0000_0011, 0)
Count the number of dark pixels (<2) in a horizontal line from X = 0 to X = 127 at height Y = 7.
Assign the count to dark.
SOURCE CODE... PUB line(px0, py0, px1, py1, pix1, pix0, condx, stats_addr) return do(LIN, px0, py0, px1, py1, pix1, pix0, condx, stats_addr) filled_box(px0, py0, px1, py1, pix1, pix0, condx, stats_addr)Draw a filled rectangle with corners at (px0, py0) and (px1, py1). As the rectangle is drawn, pixel values are replaced with pix1 or pix0, depending on whether its corresponding mask bit is a 1 or 0, respectively. If the replacement value is UNDEF, the pixel value is not replaced.px0: X coordinate of the rectangle's first corner.
py0: Y coordinate of the rectangle's first corner.
px1: X coordinate of the rectangle's opposite corner.
py1: Y coordinate of the rectangle's opposite corner.
pix1: The value to replace 1-masked pixels with, or UNDEF for no replacements.
pix0: The value to replace 0-masked pixels with, or UNDEF for no replacements.
condx: The 16-bit mask used to determine pixel type (high bit for 15; low bit for 0).
stats_addr: Address of a 22-word word-aligned array, or zero. (See do below for an explanation.)
Return: The number of 1-masked pixels encountered.
Example: count := img.draw_rectangle(0, 0, 127, 95, 15, img#UNDEF, %1111_1111_0000_0000, @img_stats)
Replace all the bright pixels (>7) in a 128x96-pixel image with white, returning the
number of bright pixels replaced and assigning it to count. Image stats are also stored in the
img_stats array.
SOURCE CODE... PUB filled_box(px0, py0, px1, py1, pix1, pix0, condx, stats_addr) return do(RECT, px0, py0, px1, py1, pix1, pix0, condx, px0 << 24 | py0 << 16 | stats_addr) outline(px, py, px0, py0, px1, py1, new1, condx, stats_addr)Trace the outline of a "blob" that contains point (px, py) and is bounded by the rectangle (px0, py0), (px1, py1). Points interior to the blob are defined by the 16-bit condx mask: Pixel values corresponding to 1's are interior points; to 0's, exterior. (Points outside the bounding rectangle are deemed to be exterior points.) Point (px,py) must be an interior point, or the function will return zero. Otherwise the function scans right for the first exterior point, then follows the boundary around the blob until a full circuit has been made. If the circuit traversed defines an interior hole instead of outside boundary, further scans to the right are made until an outside boundary is found. The function will then return the number of boundary points traversed (i.e. the perimeter of the "blob"). Perimeter points may be replaced by the value new1, unless it is UNDEF. new1 should be an "interior" value, or else this function will malfunction. (See "do" below for a description of stats_addr.)Parameters:
px: X coordinate of starting point, interior to the blob.
py: Y coordinate of starting point, interior to the blob.
px0: X coordinate of the bounding rectangle's first corner.
py0: Y coordinate of the bounding rectangle's first corner.
px1: X coordinate of the bounding rectangle's opposite corner.
py1: Y coordinate of the bounding rectangle's opposite corner.
new1: Value to replace perimeter pixels with, or UNDEF.
condx: The 16-bit mask used to determine interior pixels (high bit for 15; low bit for 0).
stats_addr: Address of a 22-word word-aligned array, or zero. (See do below for an explanation.)
Return: The perimeter of the blob.
Example: img.outline(50, 50, 0, 0, 127, 95, 0, %0000_0000_1111_1111, @img_stats)
Starting at interior point (50, 50) in a bounding box defined by corners (0, 0), (127, 95) find and
follow an edge between interior dark pixels (<8) and exterior bright pixels. Replace the perimeter
pixels with black (0). Record the image stats in the array img_stats.
SOURCE CODE... PUB outline(px, py, px0, py0, px1, py1, new1, condx, stats_addr) : retn stats_addr &= $ffff px &= $ff py &= $ff repeat while (retn := do(BORDER, px0, py0, px1, py1, UNDEF, UNDEF, condx, px << 24 | py << 16 | stats_addr)) < 0 do(LIN | FIND_ONE, px, py, px1, py, UNDEF, UNDEF, !condx, @stats) do(LIN | FIND_ONE, py, px1, py, UNDEF, UNDEF, condx, stats[16], @stats) px := stats[16] if new1 <> UNDEF retn := do(BORDER, px0, py0, px1, py1, new1, UNDEF, condx, px << 24 | py << 16 | stats_addr) do(comd, px0, py0, px1, py1, pix1, pix0, condx, stats_addr)This is the general user-accessible function to use when the above convenience functions fall short. It iterates over the chosen area, optionally replacing pixel values as it goes. If a non-zero word address is provided in the lower 16 bits of stats_addr, it will fill the array there with information about the area traversed.Parameters:
comd: LIN, RECT, or BORDER, optionally ORed with EODIF and/or FIND_ONE. This
selects the type and behavior of the iterator.
The LIN iterator covers a straight line beginning at (px0, py0) and ending at (px1, py1).
The RECT iterator covers a rectangular area bounded by (px0, py0) and (px1, py1). An optional
starting point can be specified in the upper 16 bits of stats_addr as x << 24 | y << 16. This
can be used to resume an iteration that was stopped with the FIND_ONE modifier. The starting
point will be (x #> px0, y #> py0), so if x and y are zero, the starting point is (px0, py0).
The BORDER iterator starts at (x, y) embedded in stats_addr, as above for RECT. It behaves as
described above for "outline" except that it does not keep searching if it finds a "hole".
In the case of a hole, it returns a negative count of the perimeter pixels.
EODIF selects even/odd differential mode. In this mode a pixel's value is taken to be absolute
value of the difference between it and its even/odd mate in the y direction. In this mode,
LIN and BORDER examine all pixels in their path; RECT examines only pixels in odd-numbered
rows to avoid redundancy and save time. Only pixels in odd-numbered rows can have their values
replaced in this mode.
FIND_ONE stops the iterator when the first pixel corresponding to a "1" in the condition mask is found.
px0: X coordinate of the bounding rectangle's first corner.
py0: Y coordinate of the bounding rectangle's first corner.
px1: X coordinate of the bounding rectangle's opposite corner.
py1: Y coordinate of the bounding rectangle's opposite corner.
new1: Value to replace perimeter pixels with, or UNDEF.
condx: The 16-bit mask used to determine interior pixels (high bit for 15; low bit for 0).
stats_addr: Optionally define the (x, y) starting point in its upper 16 bits, and a word-aligned address
for a stats array in its lower 16 bits. The stats, if specified, will be written to 22 words, beginning
at the specified address as follows:
Words 0 - 15 (histogram): A count of how many pixels examined have each corresponding value.
Words 16 - 19 (bounding box): Corner coordinates (xleft, ytop), (xright, ybottom) of the smallest
rectangle containing the examined pixels whose intensities correspond to ones in the condition mask.
Words 20 - 21 (centroid): The average coordinates (xavg, yavg) of the examined pixels whose intensities
correspond to ones in the condition mask.
Return: The number of iterated-over pixels whose intensities correspond to ones in the condition mask.
Example: changes := img.do(img#RECT | img#EODIF, 0, 0, 127, 95, img#UNDEF, %1111_1111_1111_0000, 0)
Count the number of pixels in even rows that differ from their odd-row counterparts by 4 or more.
Assign the result to changes.
SOURCE CODE... PUB do(comd, px0, py0, px1, py1, pix1, pix0, condx, stats_addr) return command(((comd | ITERATE) & $ff | WR1 & (pix1 <> UNDEF) | WR0 & (pix0 <> UNDEF)) << 24 | (pix1 & $0f) << 20 | (pix0 & $0f) << 16 | condx & $ffff, px0 << 24 | (py0 & $ff) << 16 | (px1 & $ff) << 8 | py1 & $ff, stats_addr) Private Spin Methodscommand(cmd_n_mask, x0y0x1y1, xy_init_stats_ptr)This private method is the direct interface to the PASM code.SOURCE CODE... PRI command(cmd_n_mask, x0y0x1y1, xy_init_stats_ptr) if started _params0 := x0y0x1y1 _params1 := xy_init_stats_ptr _command := cmd_n_mask repeat while _command return _params0 else return 0 PASM Code/Class VariablesSOURCE CODE... org 0 entry mov xy_ptr,par add xy_ptr,#4 mov ptr_ptr,par add ptr_ptr,#8 get_cmd rdlong cmd,par wz if_z jmp #get_cmd rdlong x0,xy_ptr mov y0,x0 mov x1,x0 mov y1,x0 shr x0,#24 shr y0,#16 and y0,#$ff shr x1,#8 and x1,#$ff and y1,#$ff mov ret_val,#0 mov mask,cmd and mask,_0xffff shr cmd,#16 mov replace0,cmd and replace0,#$0f mov replace1,cmd shr replace1,#4 and replace1,#$0f shr cmd,#8 rdlong out_ptr,ptr_ptr cmp cmd,#SETBUF wz if_nz jmp #chkrange rdword high,mask add mask,#2 rdword wide,mask mov dwide,wide shr dwide,#1 add mask,#2 mov buf_addr,mask jmp #all_done chkrange cmp x0,wide wc if_c cmp y0,high wc if_c cmp x1,wide wc if_c cmp y1,high wc if_nc jmp #all_done '_________________________________________________________ do_iterate mov x,x0 mov y,y0 and cmd,#$7f cmp cmd,#RECT wc if_nc call #start_xy call #pixaddr mov difx,x1 sub difx,x0 wc,wz if_z mov dx,#0 if_nz negc dx,#1 if_nz negc difx,difx mov countx,difx add countx,#1 mov dify,y1 sub dify,y wc,wz if_z mov dy,#0 if_nz negc dy,#1 if_nz negc dify,dify mov county,dify add county,#1 negc dyaddr,dwide movd :clr_lp,#histo mov s0,#22 :clr_lp mov 0-0,#0 add :clr_lp,_0x200 djnz s0,#:clr_lp sub minx,#1 sub miny,#1 cmp cmd,#RECT wc if_c jmp #iter_line cmp cmd,#BORDER wc if_c jmp #iter_rect cmp cmd,#NO_OP wc if_c jmp #iter_outline jmp #done '_________________________________________________________ iter_line min countx,county mov s0,difx shr s0,#1 mov s1,dify shr s1,#1 mov s2,difx max s2,dify :loop call #do_opr sub s0,s2 wc,wz if_c_or_z add s0,difx if_c_or_z add pix_addr,dyaddr if_c_or_z add y,dy sub s1,s2 wc,wz if_nc_and_nz jmp #:next add s1,dify test x,#1 wz mov dx,dx wc if_c_eq_z add pix_addr,dx add x,dx :next djnz countx,#:loop jmp #done '_________________________________________________________ iter_rect mov s2,dyaddr mov s0,x0 shr s0,#1 add dyaddr,s0 mov s0,x1 shr s0,#1 sub dyaddr,s0 test y,#1 wz test cmd,#EODIF wc if_nz_or_nc jmp #:start_ok add y,dy mov x,x0 sub county,#1 call #pixaddr :start_ok mov countx,x1 sub countx,x abs countx,countx jmp #:starty :loopy test y,#1 wz test cmd,#EODIF wc if_z_and_c add pix_addr,s2 if_z_and_c jmp #:next_y mov x,x0 mov countx,difx :starty add countx,#1 jmp #:do_opr :loopx test x,#1 wz mov dx,dx wc if_c_eq_z add pix_addr,dx add x,dx :do_opr call #do_opr djnz countx,#:loopx add pix_addr,dyaddr :next_y add y,dy djnz county,#:loopy jmp #done '_________________________________________________________ iter_outline call #try_pix 'Does the pixel at (x,y) meet the blob conditions? if_z jmp #done ' No: Nothing left to do. :find_edge call #move_rt ' Yes: Move one pixel to the right. Still inside the blob? if_nz jmp #:find_edge ' Yes: Do it again. call #move_lf ' No: Move back onto the blob pixel. mov s0,#2 ' Set initial direction to 'left'. mov dify,#0 ' Clear winding number. :follow_lp sub s0,#1 'Following outline with right hand on wall. Try turning right first. and s0,#3 mov s1,s0 'Remember direction. mov s2,#1 'First turn is right for winding number. :turn_lp call #move 'Move to an adjacent pixel. Is it "blob"? if_nz jmp #:inside call #move_back ' No: Move back to blob pixel. sub s2,#1 add s0,#1 ' Try next direction to the left of what we tried. and s0,#3 cmp s0,s1 wz ' Have we already tried this direction? if_nz jmp #:turn_lp ' No: Go back and try it. call #do_pix1 ' Yes: Stuck in a one-pixel blob. What are the odds? jmp #:done ' Nowhere else to go, so quit. :inside test ret_val,ret_val wz ' Yes: Have we processed a blob pixel yet? if_nz jmp #:not_first mov dx,x ' No: This is the first. Remember this position... mov dy,y mov difx,s0 ' ...and how we got here. jmp #:do_pix :not_first sub dify,s2 ' Yes: Add turn value to winding number. cmp x,dx wz ' Did we start here... if_z cmp y,dy wz if_z cmp s0,difx wz ' ...coming from the same direction? if_z jmp #:done ' Yes: We're done. :do_pix call #do_pix1 ' Process this pixel. jmp #:follow_lp ' Follow the wall some more. :done mov dify,dify wc ' Return perimeter with sign of winding number. negc ret_val,ret_val '_________________________________________________________ done wrlong ret_val,xy_ptr and out_ptr,_0xffff wz if_z jmp #all_done mov s0,avgx mov s1,ret_val call #divide mov avgx,s0 mov s0,avgy mov s1,ret_val call #divide mov avgy,s0 movd :xfer_loop,#histo mov s0,#22 :xfer_loop wrword 0-0,out_ptr add :xfer_loop,_0x200 add out_ptr,#2 djnz s0,#:xfer_loop all_done wrlong zero, par jmp #get_cmd '_________________________________________________________ do_opr call #get_pix add p1,#histo movd :inc_histo,p1 sub p1,#histo :inc_histo add 0-0,#1 if_z jmp #do_pix0 do_pix1 add ret_val,#1 add avgx,x add avgy,y min maxx,x max minx,x min maxy,y max miny,y test cmd,#WR1 wz if_z jmp #_qonly mov p1,replace1 jmp #_write do_pix0 test cmd,#WR0 wz if_z jmp do_opr_ret mov p1,replace0 _write test y,#1 wc test cmd,#EODIF wz if_nc_and_nz jmp do_opr_ret andn pixpair,#$0f or pixpair,p1 test x,#1 wz if_nz rol pixpair,#4 wrbyte pixpair,pix_addr cmp ret_val,#1 wc if_c jmp do_opr_ret _qonly test cmd,#FIND_ONE wz if_nz jmp #done do_opr_ret do_pix1_ret do_pix0_ret ret '_________________________________________________________ move_back xor s0,#2 call #move xor s0,#2 move_back_ret ret move and s0,#3 wz if_z jmp #move_rt cmp s0,#1 wz if_z jmp #move_up cmp s0,#2 wz if_z jmp #move_lf move_dn add y,#1 add pix_addr,dwide jmp #try_pix move_rt add x,#1 test x,#1 wz if_z add pix_addr,#1 jmp #try_pix move_lf sub x,#1 test x,#1 wz if_nz sub pix_addr,#1 jmp #try_pix move_up sub y,#1 sub pix_addr,dwide try_pix test x,#0 wz cmp x,x0 wc if_nc cmp x1,x wc if_nc cmp y,y0 wc if_nc cmp y1,y wc if_c jmp try_pix_ret get_pix call #_get_pix test cmd,#EODIF wc if_nc jmp get_pix_ret mov p0,pixpair mov p3,p1 test y,#1 wc sumc pix_addr,dwide call #_get_pix sumnc pix_addr,dwide sub p1,p3 abs p1,p1 call #test_pix mov pixpair,p0 move_ret move_rt_ret move_lf_ret move_up_ret move_dn_ret try_pix_ret get_pix_ret ret _get_pix rdbyte pixpair,pix_addr test x,#1 wz if_nz ror pixpair,#4 mov p1,pixpair and p1,#$0f test_pix mov p2,#1 shl p2,p1 test p2,mask wz test_pix_ret _get_pix_ret ret '_________________________________________________________ start_xy mov x,out_ptr shr x,#24 mov y,out_ptr shr y,#16 and y,#$ff cmp x1,x0 wc if_nc min x,x0 if_c max x,x0 if_c min x,x1 if_nc max x,x1 cmp y1,y0 wc if_nc min y,y0 if_c max y,y0 if_c min y,y1 if_nc max y,y1 start_xy_ret ret '_________________________________________________________ pixaddr mov pix_addr,y mov p2,#16 shl wide,#16 shr pix_addr,#1 wc :mult_lp if_c add pix_addr,wide wc rcr pix_addr,#1 wc djnz p2,#:mult_lp add pix_addr,x shr pix_addr,#1 add pix_addr,buf_addr shr wide,#16 pixaddr_ret ret '_________________________________________________________ divide shl s1,#15 mov s2,#16 :loop cmpsub s0,s1 wc rcl s0,#1 djnz s2,#:loop and s0,_0xffff divide_ret ret '_________________________________________________________ zero long 0 _0x200 long $200 _0xffff long $ffff _command long 0 _params0 long 0 _params1 long 0 cmd res 1 xy_ptr res 1 ptr_ptr res 1 replace0 res 1 replace1 res 1 mask res 1 x0 res 1 y0 res 1 x1 res 1 y1 res 1 x res 1 y res 1 dx res 1 dy res 1 dyaddr res 1 dwide res 1 difx res 1 dify res 1 countx res 1 county res 1 pix_addr res 1 s0 res 1 s1 res 1 s2 res 1 pixpair res 1 p0 res 1 p1 res 1 p2 res 1 p3 res 1 buf_addr res 1 wide res 1 high res 1 ret_val res 1 out_ptr res 1 histo res 16 minx res 1 miny res 1 maxx res 1 maxy res 1 avgx res 1 avgy res 1 License┌──────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────┤ │Permission is hereby granted, free of charge, to any person obtaining a copy of this │ │software and associated documentation files (the "Software"), to deal in the Software │ │without restriction, including without limitation the rights to use, copy, modify, │ │merge, publish, distribute, sublicense, and/or sell copies of the Software, and to │ │permit persons to whom the Software is furnished to do so, subject to the following │ │conditions: │ │ │ │The above copyright notice and this permission notice shall be included in all copies │ │or substantial portions of the Software. │ │ │ │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, │ │INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A │ │PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT │ │HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF │ │CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE │ │OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ └──────────────────────────────────────────────────────────────────────────────────────┘ |