* Colors.a - Copyright © June 1988 by Tom Krehbiel.  All rights reserved.
*
*   This program is in the public domain.  It may be freely distrubuted
* as long as 1) no money is charged for said distribution and 2) ALL of
* the files listed below are included in the distrubution.  I am including
* the source code so budding 68K assembly programmers can have some
* example code to look at (this is one of my first, so it's probably not
* very great in the efficiency department).  Feel free to make modifications
* and enhancements, just please leave my name in it someplace.
*
*   This was assembled using A68K, a public domain 68000 assembler that works
* very well.  If you want to try assembly language but don't want to run
* out and get the Metacomco assembler, check out A68K.
*
* A simple change-colors program.  Call as follows:
*
*       colors rgb rgb rgb rgb          ; sets colors immediately
*  OR:
*       colors -filename                ; looks in file 'filename' for colors
*  OR:
*       click on a colors icon that has a TOOLTYPES of:
*         COLORS=rgb rgb rgb rgb
*       and a DEFAULT TOOL of the Colors program (see examples).
*
* NOTE:  if there was some kind of error (like not being able to open a
* file or something like that), Colors WILL NOT tell you about it.  It
* will simply do nothing.  If somehow the color definitions it finds
* are outlandish (like if it finds a color file, but there's nothing in
* it), it will make every color white (or something similar).

* Include all the appropriate files:

                include "exec/types.i"
                include "exec/tasks.i"
                include "exec/libraries.i"
                include "intuition/screens.i"
                include "libraries/dos.i"
                include "libraries/dosextens.i"
                include "workbench/startup.i"
                include "workbench/workbench.i"

* helpful macros:

* This macro is for external system declarations
xlib            macro
                xref _LVO\1
                endm

push            macro                           ; (okay, so I'm lazy...)
                movem.l \1,-(A7)
                endm

pop             macro
                movem.l (A7)+,\1
                endm

* This macro is for calling exec system routines

exec            macro
                move.l  _AbsExecBase,a6         ; move exec base to reg. a6
                jsr     _LVO\1(a6)              ; call exec routine
                endm

* This macro is for calling graphics libraries
* Assumes graphics base pointer is in GBase

gfx             macro
                push    a6                      ; save reg. a6 on stack
                move.l  GBase,a6                ; move gfx base to reg a6
                jsr     _LVO\1(a6)              ; call gfx routine
                pop     a6                      ; restore reg a6
                endm

* This macro is for calling intuition libraries
* Assumes that the intuition base pointer is in IBase

int             macro
                move.l  IBase,a6
                jsr     _LVO\1(a6)
                endm

* Ditto for dos calls

dos             macro
                move.l  _DosBase,a6
                jsr     _LVO\1(a6)
                endm

* Ditto for Workbench calls

wb              macro
                move.l  WBbase,a6
                jsr     _LVO\1(a6)
                endm


                xref    _AbsExecBase            ; exec base addr

; external library references - these must be declared so the assembler
; knows about them.

                xlib    Output                  ; dos library functions
                xlib    Write
                xlib    Open
                xlib    Read
                xlib    Close
                xlib    CurrentDir

                xlib    OpenLibrary             ; exec library functions
                xlib    CloseLibrary
                xlib    Forbid
                xlib    FindTask
                xlib    WaitPort
                xlib    GetMsg
                xlib    ReplyMsg

                xlib    GetScreenData           ; intuition library function

                xlib    SetRGB4                 ; graphics library function

                xlib    GetDiskObject           ; workbench library functions
                xlib    FindToolType
                xlib    FreeDiskObject


* Main entry point here.  a0 contains a pointer to the command line, and
* d0 is the length of that command line.  Note that the program will start
* executing at the first instruction of the first object module you put
* in the list of files for the linker (BLink, at least).

Colors:
                move.l  d0,CmdLen
                push    a0              ; save pointer to command line

                bsr     open_libs

                suba.l  a1,a1           ; get address of this task
                                        ;  ('suba.l a1,a1' just clears a1)
                exec    FindTask
                move.l  d0,a4

                tst.l   pr_CLI(a4)      ; program launched from WB?
                beq     fromWB          ; yes - get tooltypes and such


WBhere:         lea     WBscreen,a0             ; point to my buffer
                move.l  #sc_SIZEOF,d0           ; size of struct Screen
                move.w  #WBENCHSCREEN,d1        ; type to look for
                int     GetScreenData           ; get WB screen info

                lea     WBscreen,a5             ; point to my buffer
                lea     sc_ViewPort(a5),a5      ; point to ViewPort addr

                clr.l   d6                      ; d6 = counter (color #)
                moveq   #4,d7                   ; d7 = another counter

                pop     a0                      ; restore command line pointer

                tst.l   wbmsg                   ; are we from WB?
                bne     do_next_color           ; yes - don't look for args

                cmpi.b  #'-',(a0)               ; load colors from a file?
                beq     get_file                ; yes, go open the file


* at this point, a0 points to a text string of the format 'rgb rgb rgb rgb'
* where 'rgb' is three hex digits.  This program only looks at the first
* four sets of 'rgb's.  Anything beyond that is ignored.

do_next_color:
                move.b  (a0)+,d0                ; get red hex value...
                bsr     hex_to_int              ;  ...convert to binary
                push    d1                      ;  ...save red component
                move.b  (a0)+,d0                ; get green hex value...
                bsr     hex_to_int              ;  ...convert to binary
                push    d1                      ;  ...save green component
                move.b  (a0)+,d0                ; get blue hex value...
                bsr     hex_to_int              ;  ...convert to binary
                push    d1                      ;  ...save blue component

                pop     d3                      ; restore components in proper
                pop     d2                      ;   regs. for the call to SetRGB4
                pop     d1

                push    a0                      ; save string pointer

                move.l  d6,d0                   ; get color number
                movea.l a5,a0                   ; get addr of WB ViewPort

                gfx     SetRGB4                 ; set this color

                addq.l  #1,d6                   ; next color number

                pop     a0                      ; get back string pointer
                lea     1(a0),a0                ; skip over the space

                subq    #1,d7                   ; continue until done.
                bne     do_next_color

exit:           tst.l   wbmsg                   ; run from WB?
                beq     oops_nofile             ; no - skip WB cleanup stuff

                move.l  a3,a0                   ; free up the RAM we
                wb      FreeDiskObject          ;  allocated w/GetDiskObject()

                exec    Forbid                  ; Forbid() so WB can't UnloadSeg() us.
                move.l  wbmsg,a1                ; reply to WB's message,
                exec    ReplyMsg                ;  telling WB we're finished.

oops_nofile:
                bsr     close_libs              ; close the libraries

                clr.l   d0                      ; signal all is well
                rts                             ; return to DOS (or WB)

* Read the colors from a file specified.  a0 points to the dash before
* the filename.  This assumes that the colors are the first thing on
* the first line of the file.  i.e. if it ain't there, you got trouble.

get_file:
                lea     1(a0),a0        ; point past the dash

                move.l  CmdLen,d0

;------ null terminate the string, eat trailing garbage
                lea   0(a0,d0.l),a1
stripjunk:
                cmpi.b   #' ',-(a1)

                dbhi   D0,stripjunk

                clr.b   1(a1)


                move.l  a0,d1                   ; open the file up
                move.l  #MODE_OLDFILE,d2
                dos     Open
                move.l  d0,infile               ; save file handle
                beq     oops_nofile             ; uh oh, file doesn't exist

                move.l  infile,d1               ; read from the file
                lea     filebuf,a1              ; where to put read chars
                move.l  a1,d2                   ; filename from command line
                move.l  #18,d3                  ; rgb rgb rgb rgb...
                dos     Read                    ;  ...(about 18 chars)


                move.l  infile,d1               ; close file (we're done)
                dos     Close

                lea     filebuf,a0              ; point to the buffer read

                bra     do_next_color           ; go parse the colors


* This code is called when Colors is run from WB.

fromWB:
                lea     4(a7),a7                ; clean stack up

                lea     pr_MsgPort(a4),a0       ; wait for message from WB
                exec    WaitPort
                lea     pr_MsgPort(a4),a0       ; there's a message, get it.
                exec    GetMsg
                move.l  d0,wbmsg                ; save it so we can reply
                move.l  d0,a1                   ;    later on.

                cmpi.l  #2,sm_NumArgs(a1)       ; check number of args
                blt     exit                    ; if less than two, problems

                move.l  sm_ArgList(a1),a2       ; get argument list
                lea     wa_SIZEOF(a2),a2        ; point to second one
                                                ;  (this should be the .info
                                                ;   file with the colors)

                move.l  wa_Lock(a2),d1          ; make the drawer it was
                dos     CurrentDir              ;  run from the current
                                                ;  directory.

                move.l  wa_Name(a2),a0          ; get the name that's there
                                                ;  (the name of the .info
                                                ;   file)

                wb      GetDiskObject           ; get it's icon info
                move.l  d0,a3                   ; save...

                move.l  do_ToolTypes(a3),a0     ; point to the tooltypes array
                lea     toolname,a1             ; a1 -> what we're looking for
                wb      FindToolType            ; look for a match
                move.l  d0,a0                   ; save it

                push    a0                      ; push on stack because
                                                ;  I pull this value up
                                                ;  above someplace.

                bra     WBhere                  ; go parse the colors


; convert a ASCII hex character in d0.b to a binary equivalent in d1.b

hex_to_int:
                cmpi.b  #'0',d0         ; is it a 0-9?
                blt     notAlpha        ; no, can't be a-f either
                cmpi.b  #'9',d0
                bgt     notDigit

                move.b  d0,d1           ; it's a digit, do a conversion
                sub.b   #'0',d1
                bra     hi_exit

notDigit:       cmpi.b  #'a',d0         ; is it an a-f?
                blt     notAlpha
                cmpi.b  #'f',d0
                bgt     notAlpha

                move.b  d0,d1           ; it's an alpha, so convert it
                sub.b   #'a',d1
                add.b   #10,d1

hi_exit:        andi.l  #$ff,d1
                rts                     ; done

notAlpha:       moveq   #-1,d1          ; -1 means error...
                bra     hi_exit

* Open all the libraries we need.

open_libs:
                lea     intui_name,a1
                moveq   #0,d0
                exec    OpenLibrary    ; open intuition.library
                move.l  d0,IBase

                lea     gfx_name,a1     ; graphics.library
                moveq   #0,d0
                exec    OpenLibrary
                move.l  d0,GBase

                lea     dosname,a1      ; dos.library
                moveq   #0,d0
                exec    OpenLibrary
                move.l  d0,_DosBase

                lea     wbname,a1       ; icon.library
                moveq.l #0,d0
                exec    OpenLibrary
                move.l  d0,WBbase

                rts

* Close all the libraries we opened above

close_libs:
                move.l  IBase,a1                ; intuition.library
                exec    CloseLibrary

                move.l  GBase,a1                ; graphics.library
                exec    CloseLibrary

                move.l  _DosBase,a1             ; dos.library
                exec    CloseLibrary

                move.l  WBbase,a1               ; icon.library
                exec    CloseLibrary

                rts


* Uninitialized data section.

                SECTION udat,BSS


IBase           ds.l    1               ; intuition base address
GBase           ds.l    1               ; graphics base address
WBbase          ds.l    1               ; icon library base address
_DosBase        ds.l    1               ; dos library base address

infile          ds.l    1               ; input file handle
filebuf         ds.b    40              ; buffer for reading the file
CmdLen          ds.l    1

WBscreen        ds.b    sc_SIZEOF       ; allocate space for screen structure


* Initialized data section.

                SECTION idat,DATA

intui_name      dc.b    'intuition.library',0
                cnop    0,2
gfx_name        dc.b    'graphics.library',0
                cnop    0,2
dosname         dc.b    'dos.library',0
                cnop    0,2
wbname          dc.b    'icon.library',0
                cnop    0,2

toolname        dc.b    'COLORS',0
                cnop    0,2

wbmsg           dc.l    0


                END                         ; all done

