PropCAM 4-bit Serial Driver┌───────────────────────────────────────┐ │ PropCAM 4-bit Serial Driver │ │ Author: Phil Pilgrim │ │(c) Copyright 2011 Bueno Systems, Inc. │ │ See end of file for terms of use. │ └───────────────────────────────────────┘ This is a 6-bit serial driver for the Parallax PropCAM. It interfaces the Kodak KAC9630 to the P8X32A Propeller chip via the sensor's serial data outputs. Revision History 2011.02.20: v0.1 Alpha release. 2013.10.06: v1.0 Initial release Contact Information Phil Pilgrim: propeller@phipi.comThis driver interfaces the PropCAM's KAC9630 image sensor to the Parallax P8X32A Propeller chip via the sensor's D0 (DATA) and D1 (bit sync, used as HSync) data pins. Data from the sensor is stored in a designated grayscale buffer, packed five pixels per long. From here it can be displayed on a video monitor and/or processed for things like motion detection, sizing, sorting, pattern recognition, etc. This object launches two additional cogs, which are used for image acquisition. In continuous acquisition modes it launches a third cog for scheduling and performing the acquisitions. It also provides the timing and user interface required for the camera's various operating modes. External ConnectionsThe PropCAM-DB, when used with this object, is plug-compatible with the Parallax Propeller Backpack (#28327) and the Parallax Spinneret (#32203). When used with other Propeller boards, connect as follows:Top View ┌─────────────────────────────┐ │ │ │ │ n/c ┼──• •─── n/c +3.3V │ DATA ┼──• •─── n/c ┬ │ n/c ┼──• •─── HSync 4.7K │ Scl ┼──• •────────────┻───── Sda │ Vsync ┼──• •─── Mclk (10 MHz) │ Gnd ┼──• •─── +3.3V │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────────┘If the Propeller module does not provide the required pull-up on Sda, you can solder a jumper across the PropCAM's J2 through-holes to deploy its onboard 4.7K pull-up. ConstantsFixed ValuesFixed characteristics of the sensor and of this driver oject.SOURCE CODE... MIN_EXP = 20 MAX_EXP = 2047 MIN_GAIN = 0 MAX_GAIN = 31 BITS_PER_PIXEL = 4 PIXEL_DEPTH = 1 << BITS_PER_PIXEL PIXELS_PER_BYTE = 8 / BITS_PER_PIXEL PIXELS_PER_LONG = 32 / BITS_PER_PIXEL PIXELS_PER_ROW = 128 / PIXELS_PER_LONG * PIXELS_PER_LONG LONGS_PER_ROW = PIXELS_PER_ROW / PIXELS_PER_LONG ROWS_PER_FRAME = 96 SAMPLES_PER_SUM = LONGS_PER_ROW * ROWS_PER_FRAME BYTE_SHIFT = (BITS_PER_PIXEL > 4) & BITS_PER_PIXEL | (BITS_PER_PIXEL =< 4) & 8 BYTE_MASK = 1 << BYTE_SHIFT - 1 Parameter ArrayIndex names for parameter array passed to the start method.SOURCE CODE... GRAY_BUF_ADDR = 0 'Address of the grayscale buffer (where the pixels get written). (Write only) LEFT_PIX = 1 'X location in buffer of leftmost pixel of acquisition area. (Write only) TOP_PIX = 2 'Y location in buffer of topmost pixel of acquisition area. (Write only) EXP_TYPE = 3 'Exposure type: FIXED, AUTOEXP, AUTOGAIN, or AUTOBOTH. (Write only) SYNC_ADDR = 4 'Address of SYNC byte from overlay object. (Write only) EXP_TIME = 5 'Exposure time in tens of microseconds (range: 20 - 2047). (Read/Write, dynamic: can be changed on the fly.) GAIN = 6 'Video amplifier log(gain) (range: 0 - 31). (Read/Write, dynamic: can be changed on the fly.) TARGET = 7 'Target average pixel value * 100 for auto modes (0 - 1500). (Write only, dynamic: can be changed on the fly.) TIME_INTERVAL = 8 'Interval (t) between snapshots ( > 0 : t seconds; < 0 : 1 / -t seconds) (Write only) AVG_PIX = 9 'Average pixel value from last snapshot * 100 (0 - 1500). (Read only) Start Routine ArgsConstants for setting argument to start methodSOURCE CODE... SNAPSHOT = 0 'Images are acquired and written on demand only. TRIGGERED = 1 'Image acquisition and writing are triggered by an edge on a port pin. (NOT YET IMPLEMENTED.) CONTINUOUS = 2 'Images are acquired one after the other as fast as possible, and grabbed and written on demand. TIMED = 3 'Images are acquired at intervals of TIME_INTERVAL, and grabbed and written on demand. SYNCED = 4 'Image acquisition is synchronized to every other vertical blanking interval of overlay object, and 'grabbed and written on demand. Exposure Type NamesConstants for setting EXP_TYPESOURCE CODE... FIXED = 0 'Fixed exposure and gain, given by EXP_TIME and GAIN. AUTOEXP = 1 'Automatically adjust EXP_TIME to keep AVG_PIX == TARGET. AUTOGAIN = 2 'Automatically adjust GAIN to keep AVG_PIX == TARGET. AUTOBOTH = 3 'Automatically adjust EXP_TIME and GAIN to keep AVG_PIX == TARGET. Acquisition Mask NamesConstants for calling acquireSOURCE CODE... GRAB_EVEN = $01 'Grab a single frame, writing even lines only. GRAB_ODD = $02 'Grab a single frame, writing odd lines only. GRAB = $03 'Grab a single frame, writing all lines. EVEN_TO_ODD = $04 'Write even camera lines to odd buffer lines. ODD_TO_EVEN = $08 'Write odd camera lines to even buffer lines. LOOP_EVEN = $10 'Grab every available frame, writing even lines only. (Ignored in SNAPSHOT mode.) LOOP_ODD = $20 'Grab every available frame, writing odd lines only. (Ignored in SNAPSHOT mode.) LOOP = $30 'Grab every available frame, writing all lines. (Ignored in SNAPSHOT mode.) Instance VariablesSOURCE CODE... long Evens[7], Odds[7], EvenSum, OddSum long Stack[64] word GrayBufAddr, ParamsAddr word Begin, Skip, Width, TakeItEven, TakeItOdd byte OddCog, EvenCog, LoopCog, TakeIt, AcqMode, Started, Lock byte Scl, Sda, Vsync, Mclk, SerData, HSync Public Spin Methodsstart(mode, pins, params)Start the image capture object. Starts two additional cogs, plus a third if CONTINUOUS mode is selected.Parameters:
mode: one of SNAPSHOT, TRIGGERED, CONTINUOUS, TIMED, or SYNCED.
pins: pointer to a byte array containing pin numbers for Scl, Sda, Vsync, Mclk, Data, and HSync, in that order.
params: the address of a 10-word parameter block. (See Parameter Array in CONstants section.)
Return: true on success; false on failure..
Example: success := img.start(img#CONTINUOUS, @PinList, @ParamList)
Start the image capture in CONTINUOUS mode (starts 3 cogs), using pins in byte array PinList,
and parameters in word array ParamList.
SOURCE CODE... PUB start(mode, pins, params) : retval | exp stop AcqMode := mode bytemove(@Scl, pins, 6) if mode <> SNAPSHOT if LoopCog := cognew(do_loop(params), @Stack) + 1 retval := true else retval := false else retval := _start(params) if retval waitcnt(cnt + clkfreq >> 3) stopStop the image capture object.Return: true if object had been Started; false if there was nothing to stop.
Example: success := img.stop
Stop the image capture object, and assign result to variable success.
SOURCE CODE... PUB stop if Started if LoopCog cogstop(LoopCog - 1) LoopCog~ if EvenCog cogstop(EvenCog - 1) EvenCog~ if OddCog cogstop(OddCog - 1) OddCog~ clear_buf ctra~ dira~ Started~ if Lock lockret(Lock - 1) return true else return false set_exp(new_exp_type, new_exp_time, new_gain, new_target)Set exposure parameters.Parameters:
new_exp_type: the type of exposure control (FIXED, AUTOEXP, AUTOGAIN, or AUTOBOTH)
new_exp_time: exposure time in tens of microseconds (20 - 2047)
new_gain: sensor gain (0 -31)
new_target: target average pixel value (normalized to 0-15), times 100.
Return: none
Example: set_exp(cam#AUTOGAIN, 1667, 15, 800)
Set to operate with automatic gain control, a fixed exposure time of 1/60th second (16670 usec),
a starting gain of 15, and a target average pixel value of 8.00 out of 15.00.
SOURCE CODE... PUB set_exp(new_exp_type, new_exp_time, new_gain, new_target) repeat while lockset(Lock - 1) word[ParamsAddr][EXP_TYPE] := new_exp_type #> FIXED <# AUTOBOTH word[ParamsAddr][EXP_TIME] := new_exp_time #> MIN_EXP <# MAX_EXP word[ParamsAddr][GAIN] := new_gain #> MIN_GAIN <# MAX_GAIN word[ParamsAddr][TARGET] := new_target #> 0 <# 1500 lockclr(Lock - 1) acquire(take)Acquire an image from the PropCAM and write it to memory.Parameters:
take: a bit-mapped parameter that determines how the image is acquired.
(See bit definitions in Acquisition Mask Names in CONstants section.)
Return: true when acquisition is complete; false if object was never Started.
Example: success := img.acquire(img#GRAB)
Grab a single frame (both even and odd lines) and store into the grayscale buffer.
Save true or false in variable success.
SOURCE CODE... PUB acquire(take) | dummy if not Started return false if AcqMode == SNAPSHOT _acquire(0, take) return true else TakeIt := take repeat while TakeIt & 3 return true Private Spin Methodsdo_loop(params)Operate PropCAM in CONTINUOUS mode as a separate cog.Parameters:
params: same as passed to start
Return: none. Starts as new cog.
SOURCE CODE... PRI do_loop(params) | timer, time0, sync_loc, pll, take case AcqMode SYNCED: sync_loc := word[params][SYNC_ADDR] wait_sync(sync_loc, 1) wait_sync(sync_loc, 0) time0 := cnt wait_sync(sync_loc, 1) time0 := (cnt - time0) << 1 CONTINUOUS: time0~ TIMED: time0 := word[params][TIME_INTERVAL] << 16 ~> 16 if time0 < 0 time0 := clkfreq / (||time0 <# 30) else time0 *= clkfreq if _start(params) timer := cnt repeat if time0 waitcnt(timer += time0) take := TakeIt | TakeIt >> 4 pll := _acquire(sync_loc, take) if (take) TakeIt &= !GRAB if AcqMode == SYNCED timer += pll * time0 >> 6 time0 += pll * 256 _start(params)Low-level start method.Parameters:
params: same as passed to start.
Return: true on success; false on failure.
SOURCE CODE... PRI _start(params) | height, i, buf_addr, left_long, top_row ParamsAddr := params left_long := word[params][LEFT_PIX] / PIXELS_PER_LONG top_row := word[params][TOP_PIX] GrayBufAddr := word[params][GRAY_BUF_ADDR] Width := word[GrayBufAddr][1] / PIXELS_PER_LONG * PIXELS_PER_LONG height := word[GrayBufAddr][0] if left_long * PIXELS_PER_LONG + PIXELS_PER_ROW > Width or top_row + ROWS_PER_FRAME > height return false GrayBufAddr += 4 Skip := Width / PIXELS_PER_LONG * 4 '108 '(Width << 2 - LONGS_PER_ROW) / PIXELS_PER_LONG * 4 '108 Begin := GrayBufAddr + (top_row * LONGS_PER_ROW + left_long ) * 4 dira := 1 << Mclk | 1 << Vsync | 1 << Scl frqa := 1 << 29 ctra := %00100 << 26 | Mclk clear_buf Evens[0] := 0 Evens[1] := Skip Evens[2] := @TakeItEven Evens[3] := 1 << SerData Evens[4] := 1 << HSync Evens[5] := 1 << Vsync Evens[6] := @EvenSum Odds[0] := 1 Odds[1] := Skip Odds[2] := @TakeItOdd Odds[3] := 1 << SerData Odds[4] := 1 << HSync Odds[5] := 1 << Vsync Odds[6] := @OddSum Started~~ if EvenCog := cognew(@entry, @Evens) + 1 and OddCog := cognew(@entry, @Odds) + 1 and Lock := locknew + 1 _config(1, %0000_0101) _config(1, %0100_0001) _config(8, $80) else stop return Started clear_bufClear the gray-level acquisition buffer to all black.Return: none.
SOURCE CODE... PRI clear_buf | i return repeat i from 0 to 95 longfill(Begin + (Width >> 1) * i, 0, 16) wait_sync(sync_loc, state)Wait for sync in overlay driver to equal state. (Sync alternates on every vertical sync.)Parameters:
sync_loc: address of the overlay driver's sync byte.
state: sync state.
Return: returns after sync state == state.
SOURCE CODE... PRI wait_sync(sync_loc, state) repeat while (byte[sync_loc] <> 0) ^ (state <> 0) _acquire(sync_loc, now)Internal acquisition method, called by acquire and by do_loop.Parameters:
sync_loc: address of image overlay sync byte.
now: lower four bits defined by Acquisition Mask in CONstants section.
SOURCE CODE... PRI _acquire(sync_loc, now) : sync | avgpix, exp, sens, targ TakeItEven := (Begin + ((WIDTH / PIXELS_PER_LONG) << 2) & (now & EVEN_TO_ODD <> 0)) & (now & GRAB_EVEN <> 0) TakeItOdd := (Begin + ((WIDTH / PIXELS_PER_LONG) << 2) & (now & ODD_TO_EVEN == 0)) & (now & GRAB_ODD <> 0) exp := word[ParamsAddr][EXP_TIME] #> MIN_EXP <# MAX_EXP sens := word[ParamsAddr][GAIN] #> MIN_GAIN <# MAX_GAIN _config(2, sens) _config(3, exp & $FF) _config(5, exp >> 8) sync := 0 outa[Vsync]~~ waitcnt(cnt + 10000) outa[Vsync]~ repeat while (TakeItEven or TakeItOdd) if byte[sync_loc] sync := 1 avgpix := (EvenSum + OddSum) * 100 / constant(SAMPLES_PER_SUM * PIXEL_DEPTH / 16) word[ParamsAddr][AVG_PIX] := avgpix <# 1500 repeat while lockset(Lock - 1) targ := word[ParamsAddr][TARGET] case word[ParamsAddr][EXP_TYPE] #> FIXED <# AUTOBOTH AUTOEXP: exp := (exp * (90 + 10 * targ / (avgpix + 1)) / 100) #> MIN_EXP <# MAX_EXP word[ParamsAddr][EXP_TIME] := exp AUTOGAIN: if avgpix < targ * 94 / 100 sens := sens + 1 <# $1f elseif avgpix > targ * 106 / 100 sens := sens - 1 #> 0 word[ParamsAddr][GAIN] := sens AUTOBOTH: if avgpix < targ * 94 / 100 and (sens < $0f or exp == MAX_EXP) sens := sens + 1 <# 31 elseif avgpix > targ * 108 / 100 and (sens > $0f or exp == MIN_EXP) sens := sens - 1 #> 0 else exp := (exp * (90 + 10 * targ / (avgpix + 1)) / 100) #> MIN_EXP <# MAX_EXP word[ParamsAddr][EXP_TIME] := exp word[ParamsAddr][GAIN] := sens lockclr(Lock - 1) if byte[sync_loc] sync |= 2 case sync 0, 1: sync := -1 2: sync := 0 3: sync := 1 return sync _config(register, data)Write configuration information to sensor chip register.Parameters:
register: sensor chip's register address.
data: byte to write in register.
Return: none
SOURCE CODE... PRI _config(register, data) outa[Scl]~~ dira[Sda]~ outa[Sda]~ dira[Sda]~~ outa[Scl]~ _write($88) _write(register) _write(data) dira[Sda]~~ outa[Scl]~~ dira[Sda]~ _write(data)Low-level I2C write method.Parameters:
data: byte to write to I2C
SOURCE CODE... PRI _write(data) repeat 8 dira[Sda] := data & $80 == 0 outa[Scl]~~ data <<= 1 outa[Scl]~ dira[Sda]~ outa[Scl]~~ repeat while ina[Sda] outa[Scl]~ PASM Code/Class VariablesSOURCE CODE... entry mov par_ptr,par rdlong whoami,par_ptr add par_ptr,#4 rdlong vskip,par_ptr add par_ptr,#4 rdlong snap,par_ptr add par_ptr,#4 rdlong _serinp,par_ptr add par_ptr,#4 rdlong _hsync,par_ptr add par_ptr,#4 rdlong _vsync,par_ptr add par_ptr,#4 rdlong sumaddr,par_ptr :frame waitpne _vsync,_vsync waitpeq _vsync,_vsync rdword snapping,snap wrword $,snap mov ptr,snapping mov line_ctr,#ROWS_PER_FRAME / 2 mov pixsum,#0 call #do_hsync tjz whoami,#:line call #do_hsync :line call #do_hsync mov ctr0,#PIXELS_PER_LONG waitpeq _hsync,_hsync :bit_lp00 test _serinp,ina wc 'b7 rcl line_buf+00,#1 test _serinp,ina wc 'b6 rcl line_buf+00,#1 test _serinp,ina wc 'b5 rcl line_buf+00,#1 test _serinp,ina wc 'b4 rcl line_buf+00,#1 nop 'b3 nop nop 'b2 nop rol line_buf+00,#24 'b1 mov ctr1,#PIXELS_PER_LONG sub ctr0,#1 wz 'b0 if_nz jmp #:bit_lp00 :bit_lp01 test _serinp,ina wc 'b7 rcl line_buf+01,#1 test _serinp,ina wc 'b6 rcl line_buf+01,#1 test _serinp,ina wc 'b5 rcl line_buf+01,#1 test _serinp,ina wc 'b4 rcl line_buf+01,#1 nop 'b3 nop nop 'b2 nop rol line_buf+01,#24 'b1 mov ctr0,#PIXELS_PER_LONG sub ctr1,#1 wz 'b0 if_nz jmp #:bit_lp01 :bit_lp02 test _serinp,ina wc 'b7 rcl line_buf+02,#1 test _serinp,ina wc 'b6 rcl line_buf+02,#1 test _serinp,ina wc 'b5 rcl line_buf+02,#1 test _serinp,ina wc 'b4 rcl line_buf+02,#1 nop 'b3 nop nop 'b2 nop rol line_buf+02,#24 'b1 mov ctr1,#PIXELS_PER_LONG sub ctr0,#1 wz 'b0 if_nz jmp #:bit_lp02 :bit_lp03 test _serinp,ina wc 'b7 rcl line_buf+03,#1 test _serinp,ina wc 'b6 rcl line_buf+03,#1 test _serinp,ina wc 'b5 rcl line_buf+03,#1 test _serinp,ina wc 'b4 rcl line_buf+03,#1 nop 'b3 nop nop 'b2 nop rol line_buf+03,#24 'b1 mov ctr0,#PIXELS_PER_LONG sub ctr1,#1 wz 'b0 if_nz jmp #:bit_lp03 :bit_lp04 test _serinp,ina wc 'b7 rcl line_buf+04,#1 test _serinp,ina wc 'b6 rcl line_buf+04,#1 test _serinp,ina wc 'b5 rcl line_buf+04,#1 test _serinp,ina wc 'b4 rcl line_buf+04,#1 nop 'b3 nop nop 'b2 nop rol line_buf+04,#24 'b1 mov ctr1,#PIXELS_PER_LONG sub ctr0,#1 wz 'b0 if_nz jmp #:bit_lp04 :bit_lp05 test _serinp,ina wc 'b7 rcl line_buf+05,#1 test _serinp,ina wc 'b6 rcl line_buf+05,#1 test _serinp,ina wc 'b5 rcl line_buf+05,#1 test _serinp,ina wc 'b4 rcl line_buf+05,#1 nop 'b3 nop nop 'b2 nop rol line_buf+05,#24 'b1 mov ctr0,#PIXELS_PER_LONG sub ctr1,#1 wz 'b0 if_nz jmp #:bit_lp05 :bit_lp06 test _serinp,ina wc 'b7 rcl line_buf+06,#1 test _serinp,ina wc 'b6 rcl line_buf+06,#1 test _serinp,ina wc 'b5 rcl line_buf+06,#1 test _serinp,ina wc 'b4 rcl line_buf+06,#1 nop 'b3 nop nop 'b2 nop rol line_buf+06,#24 'b1 mov ctr1,#PIXELS_PER_LONG sub ctr0,#1 wz 'b0 if_nz jmp #:bit_lp06 :bit_lp07 test _serinp,ina wc 'b7 rcl line_buf+07,#1 test _serinp,ina wc 'b6 rcl line_buf+07,#1 test _serinp,ina wc 'b5 rcl line_buf+07,#1 test _serinp,ina wc 'b4 rcl line_buf+07,#1 nop 'b3 nop nop 'b2 nop rol line_buf+07,#24 'b1 mov ctr0,#PIXELS_PER_LONG sub ctr1,#1 wz 'b0 if_nz jmp #:bit_lp07 :bit_lp08 test _serinp,ina wc 'b7 rcl line_buf+08,#1 test _serinp,ina wc 'b6 rcl line_buf+08,#1 test _serinp,ina wc 'b5 rcl line_buf+08,#1 test _serinp,ina wc 'b4 rcl line_buf+08,#1 nop 'b3 nop nop 'b2 nop rol line_buf+08,#24 'b1 mov ctr1,#PIXELS_PER_LONG sub ctr0,#1 wz 'b0 if_nz jmp #:bit_lp08 :bit_lp09 test _serinp,ina wc 'b7 rcl line_buf+09,#1 test _serinp,ina wc 'b6 rcl line_buf+09,#1 test _serinp,ina wc 'b5 rcl line_buf+09,#1 test _serinp,ina wc 'b4 rcl line_buf+09,#1 nop 'b3 nop nop 'b2 nop rol line_buf+09,#24 'b1 mov ctr0,#PIXELS_PER_LONG sub ctr1,#1 wz 'b0 if_nz jmp #:bit_lp09 :bit_lp10 test _serinp,ina wc 'b7 rcl line_buf+10,#1 test _serinp,ina wc 'b6 rcl line_buf+10,#1 test _serinp,ina wc 'b5 rcl line_buf+10,#1 test _serinp,ina wc 'b4 rcl line_buf+10,#1 nop 'b3 nop nop 'b2 nop rol line_buf+10,#24 'b1 mov ctr1,#PIXELS_PER_LONG sub ctr0,#1 wz 'b0 if_nz jmp #:bit_lp10 :bit_lp11 test _serinp,ina wc 'b7 rcl line_buf+11,#1 test _serinp,ina wc 'b6 rcl line_buf+11,#1 test _serinp,ina wc 'b5 rcl line_buf+11,#1 test _serinp,ina wc 'b4 rcl line_buf+11,#1 nop 'b3 nop nop 'b2 nop rol line_buf+11,#24 'b1 mov ctr0,#PIXELS_PER_LONG sub ctr1,#1 wz 'b0 if_nz jmp #:bit_lp11 :bit_lp12 test _serinp,ina wc 'b7 rcl line_buf+12,#1 test _serinp,ina wc 'b6 rcl line_buf+12,#1 test _serinp,ina wc 'b5 rcl line_buf+12,#1 test _serinp,ina wc 'b4 rcl line_buf+12,#1 nop 'b3 nop nop 'b2 nop rol line_buf+12,#24 'b1 mov ctr1,#PIXELS_PER_LONG sub ctr0,#1 wz 'b0 if_nz jmp #:bit_lp12 :bit_lp13 test _serinp,ina wc 'b7 rcl line_buf+13,#1 test _serinp,ina wc 'b6 rcl line_buf+13,#1 test _serinp,ina wc 'b5 rcl line_buf+13,#1 test _serinp,ina wc 'b4 rcl line_buf+13,#1 nop 'b3 nop nop 'b2 nop rol line_buf+13,#24 'b1 mov ctr0,#PIXELS_PER_LONG sub ctr1,#1 wz 'b0 if_nz jmp #:bit_lp13 :bit_lp14 test _serinp,ina wc 'b7 rcl line_buf+14,#1 test _serinp,ina wc 'b6 rcl line_buf+14,#1 test _serinp,ina wc 'b5 rcl line_buf+14,#1 test _serinp,ina wc 'b4 rcl line_buf+14,#1 nop 'b3 nop nop 'b2 nop rol line_buf+14,#24 'b1 mov ctr1,#PIXELS_PER_LONG sub ctr0,#1 wz 'b0 if_nz jmp #:bit_lp14 :bit_lp15 test _serinp,ina wc 'b7 rcl line_buf+15,#1 test _serinp,ina wc 'b6 rcl line_buf+15,#1 test _serinp,ina wc 'b5 rcl line_buf+15,#1 test _serinp,ina wc 'b4 rcl line_buf+15,#1 nop 'b3 nop nop 'b2 nop rol line_buf+15,#24 'b1 mov ctr0,#LONGS_PER_ROW sub ctr1,#1 wz 'b0 if_nz jmp #:bit_lp15 :write movs :slice,#line_buf nop :slice mov acc,0-0 rol acc,#4 test snapping,snapping wz if_nz wrlong acc,ptr and acc,#$0f add pixsum,acc add ptr,#4 add :slice,#1 djnz ctr0,#:slice :nxtline add ptr,vskip djnz line_ctr,#:line wrlong pixsum,sumaddr wrword zero,snap jmp #:frame do_hsync neg time,cnt waitpeq _hsync,_hsync add time,cnt cmp time,#100 wc if_c jmp #do_hsync do_hsync_ret ret zero long 0 line_buf res 16 par_ptr res 1 whoami res 1 vskip res 1 _vsync res 1 _hsync res 1 _serinp res 1 snap res 1 snapping res 1 line_ctr res 1 acc res 1 ctr0 res 1 ctr1 res 1 ptr res 1 time res 1 sumaddr res 1 pixsum res 1 fit 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. │ └──────────────────────────────────────────────────────────────────────────────────────┘ |