; jpegAGA 2.0 utility function written by Günther Röhrich
; this code is Public Domain and may be used with other programs

; this version is for 68020+ processors only
; it is is called from C:
; EncodeHAM8(char *orig, char *yham, int xsize);
; orig        = original row in RGBRGB... format
; yham        = row in HAM8 chunky format
; xsize       = size of row in pixels


 MACHINE MC68020

;NOTE: GCC stores all data as 32 bit at subroutine calls!

orig         EQU 44  ;pointer to original row (RGBRGB)
yham         EQU 48  ;pointer to actual row (HAM)
xsize        EQU 52  ;number of pixels

    
        XREF _Mult_Table  ;address of multiplication table
        XDEF _EncodeHAM8  ;entry point for function
        XREF _ColorCache ;an 256K array
        XREF _ColorTable ;an array with 64 colors

*       XDEF _blue_left ;only for debugging purposes
*       XDEF _search_finish ;only for debugging
*       XDEF _MaxError
*       XDEF _MaxErrorPos 
*       XDEF _hit       ;only for debugging
*       XDEF _t1        ;only for debugging        

               dseg

 cnop 0,4
_blue_left:      dc.b 0  ;this order is better for HAM-encoding
_red_left:       dc.b 0
_green_left:	 dc.b 0,0 ;additional dummy-value for faster longword access

 cnop 0,2
_offset_orig:   dc.w 0
_offset_ham:    dc.w 0
_CacheOffset    dc.l 0
_colcount       dc.w 2

		cseg

;register usage:   D0 = general purpose register
;                  D1 = contains afterwards the error
;                  D2 = orig_blue
;                  D3 = orig_red
;                  D4 = orig_green
;                  D5 = color that should be set
;                  D6 = offset to actual pixel (HAM)
;                  D7 = offset for color table / (0,1,2) at HAM
;                  A0 = best color so far (color+1)*3
;                  A1 = pointer to multiplication table
;                  A2 = pointer to color table / to _blue_left
;                  A3 = smallest error that has been reached so far
;                  A4 = used by the Aztec assembler (small data model)
;                  A5 = pointer to original row (RGBRGB...)
;                  A6 = pointer to the actual row (HAM)



;computing the difference of color values is done with signed 8 bit
;arithmetic 

;the maximum error value is 63^2+63^2+63^2=11907
;the summation has to be done therefore with 16 bits


;Initializing
_EncodeHAM8:    movem.l D2-D7/A2-A3/A5/A6,-(A7)  ;store registers
                lea     _blue_left,A0      ;load start of left colors
                lea     _ColorTable,A2
                move.l  (A2),(A0)          ;initialize left colors
                moveq.l #0,D6              ;initialize ham offset  
                move.l  yham(sp),A6
                move.l  orig(sp),A5                           
                lea     _Mult_Table,A1
                lea     255*2(A1),A1              
search_begin:   move.b  (A5)+,D3           ;load registers with original colors
                lsr.b   #2,D3
                move.b  (A5)+,D4           ;for faster access
                lsr.b   #2,D4
                move.b  (A5)+,D2
                lsr.b   #2,D2
                move.w  #15000,A3          ;dummy-value for minimum error so far
                lea     _ColorTable,A2
                moveq.l #0,D7              ;initialize offset for color table

;find out if the new pixel has the same color as the previous one

                lea      _blue_left,A0
                cmp.b    (A0),D2
                bne.s    search_cont
                cmp.b    1(A0),D3
                bne.s    search_cont
                cmp.b    2(A0),D4
                bne.s    search_cont

;the new pixel has the same color, let's do a special encoding

                move.w   _colcount,D7
                move.b   0(A0,D7.w),D5      
                subq.w   #1,_colcount
                bpl.s    ham6_9
                move.w  #2,_colcount
                bra.s    ham6_9
                      


;First take a look if we have the value already in the cache
search_cont:    moveq.l  #0,D0
                moveq.l  #0,D1             
                move.b   D3,D0             ;compute the cache offset
                lsl.l    #6,D0
                or.b     D4,D0
                lsl.l    #6,D0
                or.b     D2,D0             ;now we have the offset in D0
                movea.l  _ColorCache,A0    ;load start address of the cache
                move.b   0(A0,D0.l),D1     ;take the value from the cache
                bne     _hit               ;jump if we have a cache hit
                
                move.l   D0,_CacheOffset   ;store the offset to avoid recomputing it
                move.l   #3,A0             ;dummy-value for best colornumber so far
                                           ; (3 means color 0)

search_start:	bsr      _compute_error
                cmp.w    D1,A3              ;A3 <= D1 ?
                bls.s    search4
                move.w   D1,A3              ;D1 is smaller than A3, store it
                move.w   D7,A0              ;store color number     
                tst.w    D1                 ;do we have the correct color ?
                beq      search5            ;then finish immediately
search4:        cmp.w    #64*3,D7            ;have we reached highest colornum ?
                bne.s    search_start       ;no, then once again                   
                                        
;A0 contains now (colornumber+1)*3
;A3 contains the error for that colornumber

_t1:            move.w   A0,D0        
                movea.l  _ColorCache,A2
                move.l   _CacheOffset,D1
                move.b   D0,0(A2,D1.l)      ;store the value in the cache                
                lea      _blue_left,A2      ;restore A2

;compute the error when using modify mode

start_ham6:     move.b   D2,D0              ;load orig_blue
                moveq.l  #0,D7              ;assume blue should be changed
                move.b   D2,D5              ;store value to change
                sub.b    _blue_left,D0      ;D0=D0-_blue_left
                bpl.s    ham6_1
                neg.b    D0                 ;make result positive
ham6_1:         move.b   D0,D1              ;store maximum error so far
                move.b   D3,D0              ;load orig_red
                sub.b    _red_left,D0       ;D0=D0-_red_left
                bpl.s    ham6_2
                neg.b    D0              
ham6_2:         cmp.b    D0,D1              ;check D1-D0
                bge.s    ham6_3             ;jump if D1>=D0
                move.b   D0,D1              ;store new maximum error
                moveq.l  #1,D7              ;assume red should be changed
                move.b   D3,D5              ;store value to change
ham6_3:         move.b   D4,D0              ;load orig_green
                sub.b    _green_left,D0     ;D0=D0-_green_left
                bpl.s    ham6_4
                neg.b    D0
ham6_4:         cmp.b    D0,D1
                bge.s    ham6_5
                move.b   D0,D1              ;store maximum error
                moveq.l  #2,D7              ;green should be changed
                move.b   D4,D5              ;store value to change
ham6_5:         lea      _blue_left,A2      
                move.b   D5,0(A2,D7.W)      ;perform change

ham_error:      moveq.l  #0,D1
                moveq.l  #0,D0
                move.b   D2,D0              ;load blue_origin
                sub.b    (A2)+,D0           ;D0 = blue_color - blue_origin
                ext.w    D0
                add.w    0(A1,D0.W*2),D1
                move.b   D3,D0              ;load red_origin
                sub.b    (A2)+,D0           ;D0 = red_color - red_origin 
                ext.w    D0
                add.w    0(A1,D0.W*2),D1
                move.b   D4,D0              ;load green_origin
                sub.b    (A2)+,D0           ;D0 = green_color - green_origin
                ext.w    D0
                add.w    0(A1,D0.W*2),D1

                cmpa.w   D1,A3              ;check what error is smaller
                bls.s    _search_finish     ;jump if colortable is better
ham6_9:         add.b    #1,D7              ;needed to get correct code
                lsl.b    #6,D7              ;HAM8-adjust                               
                or.b     D5,D7
                move.b   D7,(A6,D6.W)       ;store code in bitmap

                addq.w   #1,D6              ;increase _offset_ham
                cmp.l    xsize(sp),D6       ;end of column ?
                bne      search_begin
                bra.s    search_end
                
_search_finish: move.w   A0,D0
                lea      _ColorTable,A2
                lea      -3(A2,D0.W),A3     ;load A3 with pointer to colors
                divu     #3,D0
                subq.w   #1,D0
                move.b   D0,0(A6,D6.W)      ;store colornumber in bitmap
                lea      _blue_left,A2
                move.l   (A3),(A2)
                addq.w   #1,D6              ;increase _offset_ham
                cmp.l    xsize(sp),D6       ;have we reached the end ?
                bne      search_begin

search_end:     movem.l  (A7)+,D2-D7/A2-A3/A5/A6  ;restore registers
                rts                         ;jump back to caller
                


;we jump here if we have reached error 0 by colortable only
;and there was not a cache hit
search5:        move.w   A0,D0         
                movea.l  _ColorCache,A2
                move.l   _CacheOffset,D1
                move.b   D0,0(A2,D1.l)      ;store the value in the cache                
                bra.s    _search_finish


;we jump here if we have a cache hit
;D1 contains the best color number, but we have to compute the error

_hit:           subq.w   #3,D1              ;correct color number
                move.w   D1,D7
                bsr.s    _compute_error
		move.w   D1,A3              ;D1 is smaller than A3, store it
                move.w   D7,A0              ;store color number     
                bra      start_ham6         ;continue with HAM encoding


;compute the error

_compute_error: moveq.l  #0,D1
                moveq.l  #0,D0             
                move.b   D2,D0              ;load blue_origin
                sub.b    0(A2,D7.W),D0      ;D0 = blue_color - blue_origin
                ext.w    D0                 ;extend result to word
                add.w    0(A1,D0.W*2),D1
                addq.w   #1,D7              ;advance color table offset
                move.b   D3,D0              ;load red_origin
                sub.b    0(A2,D7.W),D0      ;D0 = red_color - red_origin
                ext.w    D0
                add.w    0(A1,D0.W*2),D1
                addq.w   #1,D7              ;advance color table offset
                move.b   D4,D0              ;load green_origin
                sub.b    0(A2,D7.W),D0      ;D0 = green_color - green_origin
                ext.w    D0
                add.w    0(A1,D0.W*2),D1  ;D1 = D1 + D0*D0
                addq.w   #1,D7              ;advance color table offset
		rts

                


