; Sample.asm
;
; This sample is meant to serve as a prototype for assembly language Pilot
; applications. It uses the standard startup code and implements a typical
; message loop that handles some common events. Several resources (e.g.,
; menu, icon) are included, a few inline and others from external files
; generated by PilRC.
;
; NOTE: For clarity's sake this code is not highly optimized.
;
; NOTE: Where possible this sample uses PalmOS APIs to do conversion between
; integers and strings (hex and decimal). The Palm APIs have some interesting
; limitations. StrAToI accepts signed 16-bit ints. StrIToA accepts signed
; 32-bit ints. StrIToH accepts signed 32-bit ints. My implementation of htoi
; accepts unsigned 32-bit ints. The least common denominator for all these
; routines is a signed 16-bit int so I've placed that limit on the
; conversions.
;
; Formatted for 8-space tabs. Assemble with "pila sample.asm" after creating
; the resources with "pilrc sample.rcp ."
;
; By Darrin Massena
; 29 Jul 96
; (updated 10 Sep 96)

; The 'Appl' directive sets the application's name and four character id.

       Appl    "Sample App", 'samp'

; Pilot.inc contains PalmOS constants, structure offsets, and API trap codes.

       include "Pilot.inc"


; Startup.inc contains a standard startup function. This function must be
; the first within an application and is called by the PalmOS after the app
; is loaded. The startup function in Startup.inc (__Startup__) calls the
; application-defined function PilotMain.

       include "Startup.inc"


; Application-defined resource ids

kidbAIB         equ             $7FFE
kidrPREF        equ             1
kidrTVER        equ             1
kidrTAIB        equ             1000
kidfMain        equ             1000
kidmMain        equ             1000

kidmAbout       equ             1002
kidcConvert     equ             1003
kidcDecimal     equ             1004
kidcHex         equ             1005

kidrAboutAlert  equ             1000
kidrInputErrorAlert equ         1001
kidrRangeErrorAlert equ         1002

; Some global variables (a5-relative)

       data

global gpfldDecimal.l                   ;pointer to the Decimal field
global gpfldHex.l                       ;pointer to the Hex field

       code

; ---------------------------------------------------------------------------
; DWord PilotMain(Word cmd, void *cmdPBP, Word launchflags)
; PilotMain is called by the startup code and implements a simple event
; handling loop.

proc PilotMain(cmd.w, cmdPBP.l, launchFlags.w)
local   err.w
local   evt.EventType

beginproc
       tst.w   cmd(a6)                 ;sysAppLaunchCmdNormalLaunch is 0
       bne     PmReturn                ;not a normal launch, bag out

               trap    #8                                              ;break on startup for debugging

       systrap FrmGotoForm(#kidfMain.w)

PmEventLoop
; Doze until an event arrives

       systrap EvtGetEvent(&evt(a6), #evtWaitForever.w)

; System gets first chance to handle the event

       systrap SysHandleEvent(&evt(a6))

       tst.b   d0                      ;handled?
       bne.s   PmEventDone             ;yep

; Menu handler gets second chance to handle the event

       systrap MenuHandleEvent(&0, &evt(a6), &err(a6))

       tst.b   d0                      ;handled?
       bne.s   PmEventDone             ;yep

; Application handler gets third chance to handle the event

       call    ApplicationHandleEvent(&evt(a6))

       tst.b   d0                      ;handled?
       bne.s   PmEventDone             ;yep

; Form handler gets fourth chance to handle the event

       call    MainFormHandleEvent(&evt(a6))

       tst.b   d0                      ;handled?
       bne.s   PmEventDone             ;yep

; Still not handled. We're not interested in it anymore so let the
; default form handler take it.

       systrap FrmGetActiveForm()
       systrap FrmHandleEvent(a0.l, &evt(a6))

; Return from PilotMain when an appStopEvent is received

PmEventDone
       cmpi.w  #appStopEvent,evt+EventType.eType(a6) ;time to stop?
       bne.s   PmEventLoop             ;nope, loop until it is

PmReturn
       moveq   #0,d0
endproc


; ---------------------------------------------------------------------------
; Boolean ApplicationHandleEvent(EventType *pevt)
; Handles these events:
;       frmLoadEvent

proc ApplicationHandleEvent(pevt.l)
beginproc
       movem.l a3,-(a7)                ;save registers we're going to trash

       movea.l pevt(a6),a0             ;a0 = pevt

; Handle frmLoadEvents

       cmpi.w  #frmLoadEvent,EventType.eType(a0) ;frmLoadEvent?
       bne     AHENotHandled           ;no

; Initialize the form and make it active

       systrap FrmInitForm(EventType.data+frmLoad.formID(a0).w)
       move.l  a0,a3                   ;save a copy of FormPtr for later

       systrap FrmSetActiveForm(a0.l)

; Get pointers to the two fields we'll be working with (decimal, hex)

       systrap FrmGetObjectIndex(a3.l, #kidcDecimal.w)
       systrap FrmGetObjectPtr(a3.l,d0.w)
       move.l  a0,gpfldDecimal(a5)     ;save the field ptr away

       systrap FrmGetObjectIndex(a3.l, #kidcHex.w)
       systrap FrmGetObjectPtr(a3.l,d0.w)
       move.l  a0,gpfldHex(a5)         ;save the field ptr away

       moveq.l #1,d0                   ;event handled
       bra.s   AHEReturn

AHENotHandled
       clr.b   d0

AHEReturn
       movem.l (a7)+,a3
endproc


; ---------------------------------------------------------------------------
; Boolean MainFormHandleEvent(EventType *pevt)
; Handles these events:
;

proc MainFormHandleEvent(pevt.l)
beginproc

       movea.l pevt(a6),a0             ;a0 = pevt
       move.w  EventType.eType(a0),d0

; Handle frmOpenEvent

       cmp.w   #frmOpenEvent,d0
       bne     MFH1

; Draw the form

       systrap FrmGetActiveForm()
       systrap FrmDrawForm(a0.l)

       moveq.l #1,d0
       bra     MFHReturn

; Handle frmMenuEvent
MFH1
       cmp.w   #menuEvent,d0
       bne     MFH2

       cmp.w   #kidmAbout,EventType.data+menu.itemID(a0)
       bne     MFHNotHandled

       systrap FrmAlert(#kidrAboutAlert.w)

       moveq.l #1,d0
       bra     MFHReturn

; Handle ctlSelectEvent
MFH2
       cmp.w   #ctlSelectEvent,d0
       bne     MFHNotHandled

; Is the convert button being clicked?

       cmp.w   #kidcConvert,EventType.data+ctlEnter.controlID(a0)
       bne     MFHNotHandled

       jsr     Convert(pc)             ;yes, convert the number
       bra     MFHReturn

MFHNotHandled
       clr.b   d0

MFHReturn
endproc


; ---------------------------------------------------------------------------
; void Convert(void)
;

proc Convert()
local   szT.12
beginproc
       movem.l d1-d3/a3,-(a7)

; Yes. Convert the number

; 1. Figure out which field was changed (if any)
       systrap FldDirty(gpfldDecimal(a5).l)
       tst.w   d0
       bne     CVT2

       systrap FldDirty(gpfldHex(a5).l)
       tst.w   d0
       beq     CVTReturn
       clr.w   d0
CVT2
       move.w  d0,d3                   ;d3 = fDecChanged

; 2. Get its text and convert it from ASCII to integer

       move.l  gpfldHex(a5),a0         ;assume Hex changed
       tst.w   d3                      ;Decimal changed?
       beq     CVT3                    ;no

       move.l  gpfldDecimal(a5),a0
CVT3
       systrap FldGetTextPtr(a0.l)

       cmp.l   #0,a0                   ;is there any text in the field?
       bne     CVT31                   ;yes

       systrap FrmAlert(#kidrInputErrorAlert.w)           ; a comment
       bra     CVTReturn

CVT31
       tst.w   d3                      ;convert from decimal?
       beq     CVT4                    ;no

; PalmOS includes a function for converting from ASCII to integer

       systrap StrAToI(a0.l)
       bra     CVT5

; Must implement our own ASCII-hex to integer converter

CVT4
       move.l  a0,a3                   ;save a copy of the char *
       systrap StrLen(a0.l)

       move.w  d0,d2                   ;convert loop counter
       subq.l  #1,d2
       sub.l   d0,d0
CVT41
       lsl.l   #4,d0
       move.b  (a3)+,d1
       sub.b   #'0',d1
       cmp.b   #9,d1
       ble     CVT42
       sub.b   #7,d1
       cmp.b   #$f,d1
       ble     CVT42
       sub.b   #$20,d1
CVT42
       or.b    d1,d0
       dbra    d2,CVT41

       cmp.l   #$10000,d0
       blt     CVT5

       systrap FrmAlert(#kidrRangeErrorAlert.w)
       bra     CVTReturn

; 3. Convert from integer to ASCII-hex or ASCII-decimal, as appropriate

CVT5
       move.l  d0,-(a7)                ;push integer
       pea.l   szT(a6)                 ;push pointer to string buffer

       tst.w   d3                      ;convert from decimal?
       bne     CVT6                    ;yes

       trap    #15
       dc.w    sysTrapStrIToA
       addq.l  #8,a7                   ;pop the args off the stack
       bra     CVT7

CVT6
       trap    #15
       dc.w    sysTrapStrIToH
       addq.l  #8,a7                   ;pop the args off the stack

CVT7
;        addq.l  #8,a7                   ;pop the args off the stack

; 4. Place the new ASCII string in the appropriate field

       move.l  gpfldDecimal(a5),a3     ;assume Hex changed
       tst.w   d3                      ;Decimal changed?
       beq     CVT8                    ;no

       move.l  gpfldHex(a5),a3
CVT8
       ; erase the old string

       systrap FldFreeMemory(a3.l)

       ; insert the new string

       systrap    StrLen(&szT(a6))


       lea.l   szT(a6),a0
       tst.w   d3                      ;converting to decimal?
       beq     CVT83                   ;yes

       ; truncate hex numbers to 4 digits

       move.w  d0,d1
       subq.w  #4,d1
       blt     CVT83
       sub.w   d1,d0
       add.w   d1,a0
       bra     CVT83

       ; strip off leading zeros
CVT82
       cmp.b   #'0',(a0)
       bne     CVT81
       addq.l  #1,a0
       subq.w  #1,d0
CVT83
       cmp.w   #1,d0
       bgt     CVT82

CVT81
       systrap FldInsert(a3.l,a0.l,d0.w)

CVTReturn
       ; Clear the dirty bits on both fields

       systrap FldSetDirty(gpfldDecimal(a5).l,#0.b)
       systrap FldSetDirty(gpfldHex(a5).l,#0.b)

       movem.l  (a7)+,d1-d3/a3
endproc

;
; Resources -----------------------------------------------------------------
;

; Application Icon Bitmap resource. Is automatically converted from Windows
; format to Pilot format and written as a 'tAIB' rather than 'Tbmp' because
; kidbAIB is a special value ($7ffe)

       res 'WBMP', kidbAIB, "Sample.bmp"

; 'pref' resource. Defines app launch flags, stack and heap size

       res 'pref', kidrPREF
       dc.w    sysAppLaunchFlagNewStack|sysAppLaunchFlagNewGlobals|sysAppLaunchFlagUIApp|sysAppLaunchFlagSubCall
       dc.l    $1000                           ; stack size
       dc.l    $1000                           ; heap size

; Form resource

       res 'tFRM', kidfMain, "tfrm03e8.bin"

; Version resource

       res 'tver', kidrTVER
       dc.b    '1.0', 0

; Menu resource

       res 'MBAR', kidmMain, "mbar03e8.bin"

; Alert resources

       res 'Talt', kidrAboutAlert, "talt03e8.bin"
       res 'Talt', kidrInputErrorAlert, "talt03e9.bin"
       res 'Talt', kidrRangeErrorAlert, "talt03ea.bin"