;; ;; PGM (Portable Gray Map) Image decoder. Reads in a PGM file and ;; outputs data in a format usable by the viewers ;; ;; Copyright 2003, Raphael Espino ;; started 17-Sep-02 ;; last updated 27-Sep-03 ;; ;; to assemble: ;; ca65 pgmdecdr.asm ;; then link with one of the viewers ;; ;; ;; decoder memory map: ;; ;; --- zero page --- ;; $0080 - $00 decoder zero page addresses ;; $00C0 - $00FF available for viewer ;; ;; ;; --- page 6 --- ;; $0600 - $061F decoder interface variables ;; $0620 - $062F viewer jump vectors ;; $0630 - $06FF available for viewer ;; ;; --- main RAM --- ;; $2000 - $2EFF code ;; $3000 - $30FF input buffer ;; $3100 - $3AFF output buffer ;; ;; $3B00 - $FFFF is guaranteed free for renderer and screen RAM while ;; decoder is running ;; ;; ---------------- renderer interface addresses ----------------------- ;; mixed case addresses can be modified by viewer, changes made to ;; these will be read by the decoder LastCol = $600 ; Last column number to display LastRow = $601 ; Last row number to display FirstCol = $602 ; Column offset (left edge of output image) pixels/8 FirstRow = $603 ; Row offset (top edge of output image) pixels/8 IocbNum = $604 ; IOCB to read jpeg data from Error = $605 ; non 0 if error ocurred decoding jpeg, set to ABORT ; for abort from viewer, error codes as shown below ;; upper case addresses are set by decoder and should not be modified ;; by viewer WIDTH = $606 ; Width of image in pixels (2 bytes) - keep together HEIGHT = $608 ; Height of image in pixels (2 bytes) - keep together STACKPT = $60A ; stack pointer at program start, use to return ; to DOS at any point VERSION = $60B ; decoder version number RERUN = $60C ; 2 bytes - restart address MEMTOP = $60E ; End of output buffer, above this is free for viewer ; (2 bytes) SKIPERR = $610 ; Error in an image with restart markers ; decoder will continue decoding and set this to non 0 BUFCOUNT = $611 ; horizontal buffer count, this is increased by 1 for ; every buffer full of data on the current row ; the last buffer full will have msb set ;; renderer jump vectors RendInit = $620 ; renderer init vector RendStart = $623 ; renderer, image data about to start RendDraw = $626 ; renderer, draw 8 lines of image RendEnd = $629 ; renderer, image finished RendUnused = $62C ; unused for now, point at an rts ;; Rest of page 6 from $630 - $6FF is available to renderer ; Error codes NOTPGM = 1 ; not a PGM format file READERR = 2 ; error reading file HEIGHT0 = 3 ; image height is 0 WIDTH0 = 4 ; image width is 0 MAXVAL0 = 5 ; max pixel value is 0 NOTEERR = 6 ; error doing a DOS NOTE command POINTERR = 7 ; error doing a DOS POINT command NOTP5 = 8 ; not a P5 format PGM file ABORT = 128 ; user/viewer aborted decoding ; ; Atari addresses ; ; ; O.S. ROM ; CIOV = 58454 ; ; O.S. RAM ; COLOR4 = 712 ICCOM = 834 ICSTA = 835 ICBAL = 836 ICBAH = 837 ICBLL = 840 ICBLH = 841 ICAX1 = 842 ICAX2 = 843 ICAX3 = 844 ; ; PRESETS ; OPEN = 3 ; open channel GETBUF = 7 ; get buffer PUTBUF = 11 ; put buffer CLOSE = 12 ; close channel POINT = 37 ; disk point command NOTE = 38 ; disk note command ;; ;; page 0 addresses used by decoder ;; ;; ;; page 0 addresses from *** upwards are available to renderer ;; curpt = 128 tmppt = 130 ;; ;; decoding buffer addresses, these can be reused by viewer ;; when decoding has finished, but not until! ;; GreyTbl = $2F00 ; 1 page lookup table TMPBUF = $3000 ; temporary buffer area IMGBUF = $3100 ; image buffer area IMGBUFSIZE = $A00 ; image buffer size PIXBUF = 320 ; number of pixels in each buffer full .addr segstart .addr segend-1 .org $2000 segstart: tsx stx STACKPT lda #RunAgain sta RERUN+1 lda #1 ; version 1 of the PGM decoder sta VERSION RunAgain: ldx STACKPT ; make sure stack pointer is reset txs ; if we are re-running lda #0 sta Error ; no error sta SKIPERR sta paramcnt lda #255 ; need to initialise this here for Get1Byte sta normpix ; to work properly jsr OpenGr ; make sure screen is open for gr.0 jsr StrOut ; display title info .BYTE 125, 155,"PGM decoder v0.1",155,0 jsr RendInit ; let renderer initialise jsr Get1Byte cmp #'P' bne @errnotpgm jsr Get1Byte cmp #'5' bne @errunsup @skipchar: jsr Get1Byte @nextchar: cmp #' ' ; ignore spaces in header beq @skipchar cmp #10 ; ignore ^J beq @skipchar cmp #13 ; ignore ^M beq @skipchar cmp #9 ; ignore ^I beq @skipchar cmp #'#' ; check for a comment bne @notcmt jsr DoComment ; handle comment jmp @skipchar @notcmt: cmp #'0' ; check for a decimal digit bcc @skipchar cmp #'9'+1 bcs @skipchar jsr GetNum ; found a decimal digit tay ; GetNum returns with next char in acc ldx paramcnt lda reslt sta hdrwidth,x inx lda reslt+1 sta hdrwidth,x inx cpx #6 bcs getdata stx paramcnt tya jmp @nextchar ;; ;; An error occured while reading file, do something about it ;; @errunsup: sta pgmtype ; display actual type in error message lda #NOTP5 ; not a P5 PGM file .byte $2C @errnotpgm: lda #NOTPGM ; not a PGM (of any type) .byte $2C noheight: lda #HEIGHT0 ; image height is 0 .byte $2C nowidth: lda #WIDTH0 ; image width is 0 .byte $2C nomaxval: lda #MAXVAL0 ; max pixel value is 0 HandleError: sta Error pha jsr OpenGr ; switch to GR.0 pla asl tax lda ErrTable-2,x tay lda ErrTable-1,x tax jsr FindLen ; find length of error message jsr PutBuf ; then display it on screen DecdrEnd: jsr RendEnd ; tell renderer we are about to finish ldx STACKPT ; make sure stack pointer is correct txs ;; return to environment jmp OpenGr ; make sure we're in GR.0 before returning ;; header has been read, run some sanity checks on header getdata: lda hdrwidth ora hdrwidth+1 beq nowidth ; image has no width lda hdrheight ora hdrheight+1 beq noheight ; image has no height lda hdrmaxval ora hdrmaxval+1 beq nomaxval ; no maximum pixel value specified jsr Divide ; calculate factor for this image lda hdrmaxval+1 bne @max2 ; this is a 2 byte per pixel PGM lda hdrmaxval cmp #255 ; this PGM has 256 grey values, can use beq @max1 ; data directly ;; This PGM has less than 256 grey values, data is held in 1 byte. ;; Generate the lookup table for the number of pixel values specified tax lda #0 sta tmppt sta tmppt+1 lda #255 @tblloop: sta GreyTbl,x sec tay lda tmppt sbc divreslt sta tmppt lda tmppt+1 sbc divreslt+1 sta tmppt+1 tya sbc divreslt+2 dex bne @tblloop lda #0 ; this PGM needs to have pixel data sta GreyTbl .byte $2C ; adjusted to 0-255 @max2: lda #1 @max1: sta normpix ldy #3 @cphdr: ; copy header data to viewer addresses lda hdrwidth,y sta WIDTH,y dey bpl @cphdr jsr RendStart ; tell viewer we are ready to send data jsr InitBuff ; initialise image buffer ;; calculate how many lines of data we need to read lda FirstRow ldx hdrheight ldy hdrheight+1 jsr CalcPixels stx @lineoffset ; number of lines to skip at start of image sty @lineoffset+1 lda hdrheight sec sbc @lineoffset tax lda hdrheight+1 sbc @lineoffset+1 tay lda LastRow sec sbc FirstRow jsr CalcPixels stx @lineimage ; number of image lines to read sty @lineimage+1 ;; calculate how many pixels out of each line we need to skip/display lda FirstCol ; figure out how many pixels we should skip ldx hdrwidth ; at the start of each line stx pixremdr ldy hdrwidth+1 sty pixremdr+1 jsr CalcPixels stx pixoffset sty pixoffset+1 jsr CalcRemdr ; update number of pixels left on line lda LastCol ; figure out how many pixels we should display sec sbc FirstCol ldx pixremdr ; find smallest of image and display width ldy pixremdr+1 jsr CalcPixels stx piximage sty piximage+1 jsr CalcRemdr ; update number of pixels left on line jsr ResetBuff ; reset buffer count and offset @skiprows: lda @lineoffset ; skip leading rows outside of image window tax ora @lineoffset+1 beq @doneskrow txa bne @nodech dec @lineoffset+1 @nodech: dec @lineoffset lda #TMPBUF sta curpt+1 lda WIDTH ldx WIDTH+1 jsr SkipPixels jmp @skiprows @doneskrow: lda @lineimage+1 bne @notlst lda @lineimage cmp #8 bcc @islst @notlst: lda #8 @islst: sta buflines lda @lineimage ; number of image lines to read sec sbc buflines sta @lineimage bcs @nodechi dec @lineimage+1 @nodechi: @nxtbuf: lda buflines sta rowline lda #IMGBUF sta curpt+1 jsr CalcBuff @rdloop: dec rowline ; do this here so that rowline will count lda rowline cmp #255 ; from 7 to 0 when calling GetNextLine beq @calldraw jsr GetNextLine jmp @rdloop @calldraw: sec ; check if there are any extra lines lda #8 ; at end of buffer, if so, then empty them sbc buflines beq @nopad sta rowline @clrlines: ;; clear out any data in empty lines at end of image buffer ldy #PIXBUF jsr ClrLine dec rowline bne @clrlines @nopad: lda piximagerem ; is this buffer the last in the current row? ora piximagerem+1 bne @ntlst asl BUFCOUNT ; flag this as the last buffer full by sec ; setting msb ror BUFCOUNT @ntlst: lda #IMGBUF ldx buflines jsr RendDraw lda Error cmp #ABORT beq @imageend inc BUFCOUNT lda BUFCOUNT ; have we finished current row of pixels? bpl @nxtbuf ; no, go do another buffer full lda @lineimage ; have we reached end of image? ora @lineimage+1 beq @imageend ; yes, finish now jsr ResetBuff ; end of current row, reset buffer jmp @doneskrow ; and go do another row @imageend: jsr RendEnd jmp RERUN @lineoffset: .word 0 ; number of lines to skip at start of image @lineimage: .word 0 ; number of lines to display for image @lineremdr: .word 0 ; number of lines left at end rowline: .byte 0 ; lines left in current row ErrTable: ; error messages table .addr @notpgm .addr @readerr .addr @hei0 .addr @wid0 .addr @maxv0 .addr @noteerr .addr @pointerr .addr @unsup @notpgm: .byte "Not a PGM",0 @readerr: .byte "Error reading file",0 @hei0: .byte "Image height is 0",0 @wid0: .byte "Image width is 0",0 @maxv0: .byte "Image max pixel value is 0",0 @noteerr: .byte "DOS note",0 @pointerr:.byte "DOS point",0 @unsup: .byte "Unsupported type P" pgmtype: .byte " ",0 AtEOF: .BYTE 0 ; has end of file been reached? ;; ;; read a comment from the header ;; DoComment: jsr Get1Byte cmp #10 ; exit on carriage return or newline beq @exit cmp #13 beq @exit cmp #' ' ; is character displayable? bcc @blnkchar cmp #125 ; chars between 32-125 are displayable bcc @dispchar @blnkchar: lda #'?' @dispchar: jsr Print1Byte jmp DoComment @exit: jsr PrintNL dorts: rts ;; ;; read and convert and ASCII number to an integer ;; GetNum: ldy #0 sty reslt sty reslt+1 @donumb: and #%00001111 ; convert from ASCII to integer clc adc reslt ; add digit to existing integer sta reslt bcc @noinhi inc reslt+1 @noinhi: jsr Get1Byte ; get next character cmp #'0' bcc dorts ; not a digit, reached end of number cmp #'9'+1 bcs dorts ; not a digit, reached end of number pha jsr Mult10 ; multiply reslt by 10 pla bne @donumb ; forced branch ;; ;; Multiply reslt by 10 ;; Mult10: asl reslt ; 10 = 8 + 2 lda reslt ; remember (reslt * 2) rol reslt+1 ldx reslt+1 asl reslt ; shifting reslt left 3 times = (reslt * 8) rol reslt+1 asl reslt rol reslt+1 clc ; now do (reslt * 2) + (reslt * 8) adc reslt sta reslt txa adc reslt+1 sta reslt+1 rts ;; ;; Save current buffer pointer position ;; SaveCurPt: lda curpt sta lastpos lda curpt+1 sta lastpos+1 rts ;; ;; Restore last buffer pointer position ;; RestoreCurPt: lda lastpos sta curpt lda lastpos+1 sta curpt+1 rts lastpos: .word 0 ; last buffer pointer position ;; ;; OPEN SCREEN FOR GR.0 ;; OpenGr: ldx #0 lda #CLOSE sta ICCOM jsr CIOV lda #E sta ICBAH lda #12 sta ICAX1 stx ICAX2 ; open for gr.0 lda #OPEN sta ICCOM jmp CIOV E: .byte "E:" reslt: .word 0 ; result of number converted from ASCII paramcnt: .byte 0 ; current parameter number read from header ;; keep hdr labels together, consecutive in memory! hdrwidth: .word 0 ; width of image, from header hdrheight: .word 0 ; height of image, from header hdrmaxval: .word 0 ; max pixel value, from header ;; keep hdr labels together, consecutive in memory! pixoffset: .word 0 ; number of pixels to skip at start of row piximage: .word 0 ; number of pixels to display on row pixremdr: .word 0 ; number of pixels to skip at end of row piximagerem: .word 0 ; number of pixels still left to display pixbuf: .word 0 ; number of pixels to read into current buffer normpix: .byte 0 ; does the pixel data for this PGM have to ; be converted to 0-255 range? ; 255 = no, 0 = 1 byte adjustment ; 1 = 2 byte adjustment buflines: .byte 0 ; number of lines in current buffer ;; ;; Display embeded string on screen ;; string data should follow StrOut call and be terminated ;; with a 0 ;; StrOut: pla ; get string address from stack tay pla tax iny bne @nin2 inx @nin2: jsr FindLen tay txa pha ; modify return address to tya ; return immediately after pha ; terminating 0 PutBuf: lda #PUTBUF ; found end of string sta ICCOM ldx #0 jmp CIOV ; let CIO display it FindLen: sty ICBAL ; point at start of string sty @loop+1 stx @loop+2 stx ICBAH ldy #0 ; no data yet sty ICBLH @loop: lda $c000,y ; - this address gets modified beq @exit ; continue until we find terminating 0 iny bne @loop ; forced branch @exit: tya sta ICBLL clc adc @loop+1 bcc @noinx inx @noinx: rts ;; ;; Print a newline ;; PrintNL: lda #155 ; print a return char ;; Use CIO to put 1 byte on screen Print1Byte: sta @dispchar jsr StrOut @dispchar: .byte 0,0 rts ;; ;; Get 1 byte of data from file, returns with carry set on error ;; Get1Byte: lda #0 tay beq getaybytes ;; ;; Get the next 256 bytes from file ;; Get256Pixels: lda hdrmaxval+1 beq @getbytes jsr @getbytes @getbytes: ldy #1 lda #0 beq getaybytes ; forced branch ;; ;; Get number of bytes specified in Acc from file ;; GetAPixels: ldx hdrmaxval+1 ; if there are 2 bytes per pixel beq @getbytes ; then read twice as much data pha ; save Acc for next time round jsr @getbytes pla @getbytes: ldy #0 getaybytes: ldx IocbNum sta ICBLL,x pha tya sta ICBLH,x lda normpix ; if pixel data doesn't need to be adjusted bmi @noadj ; load directly to destination lda #TMPBUF sta ICBAH,x sta tmppt+1 pla jmp @getbuf @noadj: lda curpt ; update current input buffer pointer sta ICBAL,x lda curpt+1 sta ICBAH,x pla clc adc curpt sta curpt tya adc curpt+1 sta curpt+1 @getbuf: lda #GETBUF sta ICCOM,X lda COLOR4 ; flash backgroud as image is being decoded clc adc #16 sta COLOR4 jsr CIOV bpl @ok jmp @err @ok: ;; Acc will contain data if Get1Byte was used, don't overwrite Acc! ldy normpix ; if pixels need adjusting, then do it now bmi @rts beq @do1byte ;; image consists of 2 bytes per pixel, with max pixel value ;; anywhere up to 65535, so convert image data to 0-255 range and ;; copy to destination lda ICBLH,x lsr lda ICBLL,x ror tay sty @lenlo ; remember number of bytes cleared @loop2b: dey sty @curbyt tya asl tay lda (tmppt),y tax iny lda (tmppt),y jsr Multiply ldy @curbyt sta (curpt),y ; clear one block of data cpy #0 bne @loop2b lda @lenlo clc adc curpt ; update pointer to reflect amount just sta curpt ; cleared bcc @noinhi inc curpt+1 @noinhi: @rts: rts ;; image consists of 1 byte per pixel, with max pixel value < 255 ;; so convert image data to 0-255 range and copy to destination @do1byte: lda ICBLH,x sta @lenhi lda ICBLL,x tay @loop2: sty @lenlo ; remember number of bytes cleared @loop: dey lda (tmppt),y tax lda GreyTbl,x sta (curpt),y ; clear one block of data cpy #0 bne @loop lda @lenlo ; if low byte is 0, then we just cleared beq @inhi2 ; a full page inc @lenhi ; otherwise don't count this as a full page! clc adc curpt ; update pointer to reflect amount just sta curpt ; cleared bcc @noinhi2 @inhi2: inc curpt+1 @noinhi2: lda @lenlo beq @inthi2 clc adc tmppt sta tmppt bcc @nointh2 @inthi2: inc tmppt+1 @nointh2: dec @lenhi lda @lenhi bne @loop2 rts @err: lda #READERR jmp HandleError @lenlo: .byte 0 @lenhi: .byte 0 @curbyt: .byte 0 ;; ;; Ignore next 256 bytes from file ;; Ign256Pixels: lda hdrmaxval+1 beq @ignbytes jsr @ignbytes @ignbytes: ldy #1 lda #0 beq ignaybytes ; forced branch ;; ;; Ignore number of bytes specified in Acc from file ;; IgnAPixels: ldx hdrmaxval+1 ; if there are 2 bytes per pixel beq @ignbytes ; then read twice as much data pha ; save Acc for next time round jsr @ignbytes pla @ignbytes: ldy #0 ignaybytes: ldx IocbNum sta ICBLL,x tya sta ICBLH,x lda #TMPBUF sta ICBAH,x lda #GETBUF sta ICCOM,X jsr CIOV bpl @rts lda #READERR jmp HandleError @rts: rts ;; ;; Get the next row of image data from file ;; GetNextLine: lda BUFCOUNT bne @getnextbuf ; if this isn't the 1st buffer in current line lda pixoffset ldx pixoffset+1 jsr SkipPixels ; skip specified number of leading bytes jmp @norestore @getnextbuf: jsr RestorePoint @norestore: jsr SaveCurPt lda pixbuf+1 beq @nowidhi ; if >= 256 bytes of image data to read jsr Get256Pixels ; read in first 256 bytes @nowidhi: lda pixbuf beq @nowidlo jsr GetAPixels ; then read in rest of image data to display @nowidlo: jsr RestoreCurPt lda #PIXBUF adc curpt+1 sta curpt+1 lda piximagerem ; if this is the last line of the last ora piximagerem+1 ; buffer of data, then skip any extra tax ora rowline ; pixels at end of image beq @doremdr txa beq @dontsave ; never need to save current point in file jsr SavePoint ; for the last buffer full @dontsave: clc lda BUFCOUNT ; if this is any line except the last one adc rowline ; of the first buffer of data bne @doremdr rts ;; Only need to do this for all but the last line of the first buffer ;; of data, and for the last line of the last buffer of data @doremdr: lda pixremdr ; skip any remaining bytes on this line clc adc piximagerem tay lda pixremdr+1 adc piximagerem+1 tax tya jmp SkipPixels ;; ;; Save current point in file ;; SavePoint: lda #NOTE ldx IocbNum sta ICCOM,x jsr CIOV bpl @ok lda #NOTEERR jmp HandleError @ok: lda rowline asl adc rowline ; carry should already be clear, rowline <= 8 tay lda ICAX3,x sta filepoints,y lda ICAX3+1,x sta filepoints+1,y lda ICAX3+2,x sta filepoints+2,y ldy #0 @err: rts ;; ;; Restore a previously saved point in the file ;; RestorePoint: ldx IocbNum lda rowline asl adc rowline ; carry should already be clear, rowline <= 8 tay lda filepoints,y sta ICAX3,x lda filepoints+1,y sta ICAX3+1,x lda filepoints+2,y sta ICAX3+2,x lda #POINT sta ICCOM,x jsr CIOV bmi @err rts @err: lda #POINTERR jmp HandleError ;; saved points in file, each point is 3 bytes worth filepoints: .res 24 ;; ;; Skip specified number of bytes from file ;; Acc = low byte of number ;; X = high byte of number ;; SkipPixels: stx @skiptmp pha txa beq @noskiphi @skloop: jsr Ign256Pixels dec @skiptmp bne @skloop @noskiphi: pla beq @rts jsr IgnAPixels @rts: rts @skiptmp: .byte 0 ;; ;; Calculate number of pixels to read from a row/column value and ;; the max width/height values. This converts the row/column values ;; to pixels and then finds the smallest of the pixel count and the ;; width/height of the image. ;; Acc = row/column value ;; X = max pixel value low ;; Y = max pixel value high ;; CalcPixels: pha lda #0 sta @calcoffset+1 pla asl ; multiply row/col values by 8 to get rol @calcoffset+1 ; pixel count asl rol @calcoffset+1 asl rol @calcoffset+1 sta @calcoffset cpx @calcoffset tya sbc @calcoffset+1 bcs @pixbigr stx @calcoffset ; if offset is bigger, then use pixel value sty @calcoffset+1 ; as the offset value. Actually we could ; just stop here as nothing will be displayed @pixbigr: ldx @calcoffset ldy @calcoffset+1 rts @calcoffset: .word 0 ; temporary storage, calculates ;; ;; Calculate how many pixels are left on current line ;; X = number of pixels to subtract low ;; Y = number of pixels to subtract high CalcRemdr: stx @remdrtmp sty @remdrtmp+1 lda pixremdr ; calculate bytes remaining on this line sec sbc @remdrtmp sta pixremdr lda pixremdr+1 sbc @remdrtmp+1 sta pixremdr+1 rts @remdrtmp: .word 0 ; number of pixels left on line ;; ;; Calculate how many pixels to read from each line in the current ;; buffer, and how many will be left to read afterwards ;; CalcBuff: ldx piximagerem ; calculate how many pixels to read from ldy piximagerem+1 ; each line into the current buffer txa ; each buffer has space for up to PIXBUF pixels cmp #PIXBUF bcc @partbuf ; < PIXBUF pixels left, do partial buffer ldx #= PIXBUF bytes left, do full buffer ldy #>PIXBUF @partbuf: stx pixbuf sty pixbuf+1 lda piximagerem ; calculate how many more pixels will be left sec ; in each line after the current buffer sbc pixbuf ; has been read sta piximagerem lda piximagerem+1 sbc pixbuf+1 sta piximagerem+1 rts ;; ;; Reset buffer count and offset ;; ResetBuff: lda piximage ; yes, reset for next row sta piximagerem lda piximage+1 sta piximagerem+1 lda #0 sta BUFCOUNT rts ;; ;; Initialise image buffer ;; InitBuff: lda #IMGBUF ; during RendInit or RendStart sta curpt+1 ldx #>IMGBUFSIZE ldy #0 ;; clear a line of data, curpt must be set up to point at area ;; X = number of bytes to clear high ;; Y = number of bytes to clear low ClrLine: lda #0 @loop2: sty @clrtmp @loop: dey sta (curpt),Y bne @loop lda @clrtmp beq @inhi inx ; don't count this as a full page! clc adc curpt sta curpt bcc @noinhi @inhi: inc curpt+1 @noinhi: lda #0 dex bne @loop2 rts @clrtmp: .byte 0 ;; ;; Divide 255 by max pixel value. This divides 255 by a 2 byte ;; value to produce a 3 byte result in fixed point format, highest ;; byte is the integer part (divreslt+2), the other 2 bytes are ;; the fractional part (divreslt+1 and divreslt). This is equivalent ;; to: 255 65536 255 * 65536 1 ;; ------ * ----- = ----------- * ----- ;; maxval 65536 maxval 65536 ;; ;; multiplying by 1/65536 is the same as dividing by 65536, which is ;; the same as shifting the bits right 16 times, which is the same ;; as shifting the binary equivalent of the decimal point left ;; 16 times. That means that of the 24 bit result, the highest ;; 8 bits are the integer part, and the lowest 16 bits are the ;; fractional part. ;; Divide: lda #255 ; divide 255.0 by value in hdrmaxval sta divreslt+2 lda #0 ; set low 2 bytes of dividend to 0 sta divreslt ; this is equivalent to 255 * 65536 sta divreslt+1 sta @divrem ldx #24 @loop: asl divreslt rol divreslt+1 rol divreslt+2 rol rol @divrem rol @divrem+1 tay cmp hdrmaxval lda @divrem sbc hdrmaxval+1 lda @divrem+1 sbc #0 bcc @lessthn ; value is less than divisor sta @divrem+1 tya sbc hdrmaxval tay lda @divrem sbc hdrmaxval+1 sta @divrem inc divreslt ; increase result by 1 @lessthn: tya dex bne @loop rts @divrem: .word 0 ; remainder of division divreslt: .byte 0,0,0 ; result of division ;; ;; Multiply a pixel value by the result of the division above, this ;; will adjust the pixel value from its original range (0-maxval) ;; to a value in the 0-255 range ;; Acc = multiplier low byte ;; X = multiplier high byte ;; ;; Returns ;; Acc = result ;; Multiply: stx @multval ldx #0 stx @reslt stx @reslt+1 ldx #16 @loop: asl @reslt rol @reslt+1 rol rol @multval bcc @noadd tay clc lda divreslt adc @reslt sta @reslt lda divreslt+1 adc @reslt+1 sta @reslt+1 tya adc #0 @noadd: dex bne @loop rts @reslt: .word 0 @multval: .byte 0 segend: ;; make sure code doesn't run into the data buffers, if it does ;; things will go badly wrong .if ( * >= (GreyTbl/256)*256) .error ("Error - Code overrun into Buffer area ", .string(*)) .else .out .concat("Code ends at ",.string(*)," Max=",.string((GreyTbl/256)*256-1)) .endif ;; file header data .SEGMENT "EXEHDR" .BYTE 255, 255 ;; run address .SEGMENT "AUTOSTRT" .ADDR 736 .ADDR 737 .ADDR segstart