load 'macros.dump' include 'driver.equ' include 'scrap.equ' include 'sssc.equ' IMPORT D_CopyStrings IMPORT X_FormatMask ENTRY X_MoveSaneX ENTRY X_CircStr ENTRY X_ErrorStr ENTRY X_NAStr ENTRY X_DecForm ENTRY X_drec ENTRY X_nstring ENTRY X_PoundStr ;---------------------------------------------------------------------------- ; X_FormatValue(Width:w,Format:l,FloatPtr:l),out=(stringwidth:w, ; StringPtr:l,color:w) ; X_FormatValue PROC EXPORT input Width:w,Format:l,FloatPtr:l output StringWidth:w,StringPtr:l,Color:w local OldFace:w,FormatWidth:w,DecPlaces:l local InsertChar:w,InsertPos:w,MinusFlag:w,FormatType:w local ParenFlag:w,RedFlag:w,CommaFlag:w,FirstNumber:w local Scratch:w,Gen2Fixed:w,DotPos:w,EPos:w,EndPos:w,ShiftPos:w error theErr BEGIN +b ; Initialize locals stz theErr stz Color stz FormatWidth MoveLong #X_NString,StringPtr stz MinusFlag stz FormatType stzl DecPlaces stz Gen2Fixed ; This code will not handle negative width's gracefully lda Width and #$7FFF sta Width ; To allow scrap conversion to call this routine and suppress parens on negative ; numbers, the following mask is performed. (The default is to mask out nothing.) lda Format and X_FormatMask sta Format lda Format+2 and X_FormatMask+2 sta Format+2 ; Fix up the font tables Tool _GetTextFace,out=(OldFace:w) lda Format bit #X_SSCellBold beq getFontID lda OldFace ora #$01 tool _SetTextFace,in=(a:w) getFontID Tool _GetFontID,out=(xy:l) tya and #$FF00 ora OldFace cpx X_CurFontID bne setupTable cmp X_CurFontID+2 beq checkSpecial setupTable jsr FillTable ; First check for special strings, like Circ., Error, and N/A. checkSpecial ldy Format ldx Format+2 txa ; x = Format+2 and #X_SSCellCircular bne doCirc tya ; y = Format and #X_SSCellInvalid beq doNormal tya ; y = Format and #X_SSCellError bne doError MoveLong #X_NAStr,StringPtr bra doSpecial doError MoveLong #X_ErrorStr,StringPtr bra redSpecial doCirc MoveLong #X_CircStr,StringPtr redSpecial MoveWord #$4444,Color doSpecial brl DoWidth ; We can assume that there really is a number to be formatted. Let's look at the ; various formatting options. doNormal txa ; x = Format+2 and #X_SSCellDecPlace sta DecPlaces tya ; y = Format and #X_SSCellComma sta CommaFlag tya ; y = Format and #X_SSCellRedNeg sta RedFlag tya ; y = Format and #X_SSCellParenNeg sta ParenFlag beq doGenFormats MoveWord X_CParenWidth,FormatWidth ; We've checked the general formats, now check the exclusive formats. doGenFormats tya ; y = Format bit #X_SSCellFixed beq chkDollar MoveWord #2,FormatType bra doPreFormat chkDollar bit #X_SSCellDollar beq chkPercent MoveWord #4,FormatType bra doPreFormat chkPercent bit #X_SSCellPercent beq chkSciNot MoveWord #6,FormatType bra doPreFormat chkSciNot bit #X_SSCellSciNote beq doPreFormat MoveWord #8,FormatType ; We have parsed all of the input, and now we want to figure out what we want ; to ask from SANE. We will massage the output later. doPreFormat Call X_MoveSANEX,in=(FloatPtr:l,#X_Float:l) ldx FormatType jsr (PreSaneTable,x) tool FX2DEC,in=(#X_DecForm:l,#X_Float:l,#X_DRec:l) tool FDEC2STR,in=(#X_DecForm:l,#X_DRec:l,#X_NString:l) ; Check for various conversion errors. cmpw [StringPtr],#$3F01 ; '?' beq goTooBig and #$FF bne stringOK ; zero string? -- actually too small goTooBig brl TooBig ; Terminate the string with a '.' stringOK inc a tay lda #'.' ; put '.' at end for search sta [StringPtr],y ; Is the value negative ldy #1 lda [StringPtr],y and #$00FF cmp #'-' bne massageString inc MinusFlag iny ; Massage the string according to the formatting desired. massageString sty FirstNumber ; first digit in string ldx FormatType jsr (PostSaneTable,x) ; Insert commas. First find out how may digits are to the left of the ; decimal pt, then count back in three's and insert commas. insertCommas lda CommaFlag beq chkNegOptions MoveWord #',',InsertChar ; character to insert ldy FirstNumber bra doDotChk countLoop ; Find the '.' That's why we iny ; terminated the string with '.' doDotChk lda [StringPtr],y and #$00FF cmp #'.' beq doneCount cmp #'e' bne countLoop doneCount dey insertCommaLoop ; Backup in three's dey dey dey bmi chkNegOptions cpy FirstNumber blt chkNegOptions iny sty InsertPos jsr InsertCharacter ldy InsertPos dey bra insertCommaLoop ; Format the negative options. chkNegOptions lda MinusFlag beq doWidth lda RedFlag beq chkParens MoveWord #$4444,Color chkParens lda ParenFlag beq doWidth stz FormatWidth ldy #1 lda [StringPtr],y ; put '(' over the existing '-' and #$FF00 ora #'(' sta [StringPtr],y lda [StringPtr] ; insert ')' at the end. and #$00FF inc a sta InsertPos MoveWord #')',InsertChar jsr InsertCharacter ; We now have the final string, let's make one last check that it fits. If it ; doesn't, return ## as the string. doWidth Tool _StringWidth,in=(StringPtr:l),out=(StringWidth:w) AddWord a,FormatWidth,StringWidth cmp Width blt Exit beq Exit ; The string is too big for the given width. Replace the number with ##. TooBig inc theErr MoveLong #X_PoundStr,StringPtr Tool _StringWidth,in=(StringPtr:l),out=(StringWidth:w) Exit Tool _SetTextFace,in=(OldFace:w) RETURN ;-------------------------------------------------------------- ; This mini-routine inserts the character in InsertChar at InsertPos, ; by start at the end, working backwards moving the characters out ; of the way one position, inserting the character, and inc-ing the len. InsertCharacter lda [StringPtr] and #$00FF tay iny insertCharLoop lda [StringPtr],y xba sta [StringPtr],y dey cpy InsertPos bge insertCharLoop iny and #$FF00 ora InsertChar sta [StringPtr],y lda [StringPtr] inc a sta [StringPtr] rts ;------------------------------------------ ;------------------------------------------ ; This table is used to determine what pre-parsing ; should be done. PreSaneTable DC.W doPreGeneral DC.W doPreFixed DC.W doPreDollar DC.W doPrePercent DC.W doPreSciNot doPrePercent ; Percent is the same as fixed except that ; the input is multiplied by 100 first. Tool FMULX,in=(#X_Sane100:l,#X_Float:l) doPreDollar ; Dollar is the same as fixed doPreFixed MoveWord #1,X_DecForm ; 1 = Fix Decimal style MoveWord DecPlaces,X_DecForm+2 ; # of digits right of dec. pt. rts ;------------------------------------------ ; In General format, we will make a conservative estimate ; of how many digits can fit in the given width. If ; we can fit a representation of the number in that many ; digits in a fixed format, do so. Else do Sci. Not. doPreGeneral stz DecPlaces ; we use this var. for # of digits ; First find out if the number is negative, and subtract the appropriate ; width for the '-' or '()'. If the number is positive, subtract ')' if necessary. ; I have special cased '0' for speed and accuracy. The rest of the opproximating ; is easier if I use the absolute value of the number. Tool FCPXX,in=(#X_Float:l,#X_Sane0:l) FBEQ doZero FBGE posNumber Tool FABSX,in=(#X_Float:l) lda ParenFlag bne addParens SubWord Width,X_MinusWidth,a bra handledMinus addParens SubWord Width,X_OParenWidth,a ; X_OParenWidth = '(' + ')' bra handledMinus ; Zero special case. Get the number in Fixed Mode and set flag for displaying ; fixed mode. doZero inc Gen2Fixed MoveWord #1,X_DecForm ; 1 = Fix Decimal style stz X_DecForm+2 ; 0 digits right of dec. pt. stz X_DRec+2 ; I need this to be 0. rts ; The number is positive, subtract width of ')' if necessary. posNumber lda Width ldx ParenFlag beq handledMinus SubWord a,X_CParenWidth,a ; We have handled any negative formats, now subtract a Decimal Pt, and start ; with the first digit. (You can't display a number with no digits.) handledMinus SubWord a,X_DotWidth,a SubWord a,X_MaxDigitWidth,a bmi gotDigits inc DecPlaces ; Now loop, subtracting one digit at a time, and seeing if we have exceeded ; the width. If comma's, then subtract the comma width every third digit. ; We will never display more then 17 digits. digitLoop SubWord a,X_MaxDigitWidth,a bmi gotDigits inc DecPlaces ldy DecPlaces ; only check here, since cpy #17 ; this is the inc that would beq gotDigits ; do it. SubWord a,X_MaxDigitWidth,a bmi gotDigits inc DecPlaces SubWord a,X_MaxDigitWidth,a bmi gotDigits ldx CommaFlag beq addPlace SubWord a,X_CommaWidth,a bmi gotDigits addPlace inc DecPlaces bra digitLoop ; DecPlaces now holds the max number of digits that we want to display. ; Can we represent it in a fixed format? ; First, we want to compare the rounded value with the maximum and minumum ; display-able number in the given number of digits. gotDigits Call X_MoveSANEX,in=(#X_Float:l,#X_Float3:l) Tool FRINTX,in=(#X_Float3:l) ; Raise 10 to the power of DecPlaces, this is the Max number displayable. ; If our value is more, display in Floating Format. Call X_MoveSANEX,in=(#X_Sane10:l,#X_Float2:l) lda DecPlaces Tool FXPWRI,in=(a:w,#X_Float2:l) Tool FCPXX,in=(#X_Float3:l,#X_Float2:l) FBGE doFloat ; Raise 1/10 to the power of DecPlaces-1, this is the Min number displayable. ; If our value is less, display in Floating Format. (This is why we special ; cased '0') Call X_MoveSANEX,in=(#X_Sane10th:l,#X_Float2:l) lda DecPlaces dec a Tool FXPWRI,in=(a:w,#X_Float2:l) Tool FCPXX,in=(#X_Float:l,#X_Float2:l) FBGE displayFixed brl doFloat2 ; We now know that we want to display the number in a fixed format. However, if ; we want to display a number larger than 1, it is easier to ask for the number in ; Float format, because we can ask for significant digits rather then number of ; digits to the right of the decimal. displayFixed inc Gen2Fixed Tool FCPXX,in=(#X_Float:l,#X_Sane1:l) FBLT setFixedRecord brl setFloat setFixedRecord MoveWord #1,X_DecForm ; 1 = Fix Decimal style dec DecPlaces MoveWord DecPlaces,X_DecForm+2 ; # of digits right of dec. pt. brl donePostGen ; The number is too big to display as a fixed number. Let's find out how many ; digits are needed for the exponent of the Sci. Not. number and subtract that ; from the number of significant digits requested from SANE. doFloat Tool FCPXX,in=(#X_Float:l,#X_SaneINF:l) ; if INF, don't loop FBNE findMag brl setFloat findMag Call X_MoveSANEX,in=(#X_Sane10:l,#X_Float2:l) magnitudeLoop dec DecPlaces Tool FXPWRI,in=(#10:w,#X_Float2:l) Tool FCPXX,in=(#X_Float:l,#X_Float2:l) FBGE magnitudeLoop bra subTwoMore ; The number is too small to display as a fixed number. Let's find out how many ; digits are needed for the exponent of the Sci. Not. number and subtract that ; from the number of significant digits requested from SANE. doFloat2 Call X_MoveSANEX,in=(#X_Sane10th:l,#X_Float2:l) magnitudeLoop2 dec DecPlaces Tool FXPWRI,in=(#10:w,#X_Float2:l) Tool FCPXX,in=(#X_Float:l,#X_Float2:l) FBLT magnitudeLoop2 ; We subtract 2 more for the 'e+' or 'e-' in the number. subTwoMore SubWord DecPlaces,#2,DecPlaces setFloat stz X_DecForm ; 0 = Floating Pt. style MoveWord DecPlaces,X_DecForm+2 ; digits = the number of sig. digits donePostGen Call X_MoveSANEX,in=(FloatPtr:l,#X_Float:l) ; to original # rts ;------------------------------------------ doPreSciNot stz X_DecForm ; 0 = Floating Pt. style lda DecPlaces ; In floating point style, inc a ; digits = the number of sta X_DecForm+2 ; significant digits, so add 1 rts ;------------------------------------------ ;------------------------------------------ ; This table is used to determine what post-SANE ; massaging should be done. PostSaneTable DC.W doPostGeneral DC.W doPostFixed DC.W doPostDollar DC.W doPostPercent DC.W doPostSciNot ;------------------------------------------ ; First thing, if the number should be in a fixed ; format, change the string in the X_DNString. doPostGeneral lda Gen2Fixed beq stripZeros ; To create the Fixed format value, we are going to construct the number ; from the Dec. Rec. First clear the length byte and set the minus character ldx #1 MoveWord #$2D00,X_NString ; hex for '-' in high byte ; Only count the '-' if the number is negative. lda X_DRec beq minusHandled inx ; Find the length of the String in the Dec Rec. minusHandled lda X_DRec+4 and #$00FF inc a sta Scratch ; pos. of first non-used char. ; Calculate the position of the '.' AddWord X_DRec+2,Scratch,DotPos ; pos. where dot should go bmi padZeros cmp #2 blt padZeros ; There is at least one digit before the '.' ldy #1 intLoop lda X_DRec+4,y sta X_NString,x inx iny cpy DotPos blt intLoop ; If there are no digits after the '.', skip ahead, else set the '.' cpy Scratch beq setLength MoveWord #$002E,X_NString:x ; hex for '.' inx bra fracLoop ; There were no digits before the '.', put a '0.' and pad with zeros. padZeros MoveWord #$2E30,X_NString:x ; hex for '0.' inx inx ldy DotPos dey beq putDigits lda #'0' padLoop sta X_NString,x inx iny bne padLoop putDigits ldy #1 ; Fill in the rest of the digits here fracLoop lda X_DRec+4,y sta X_NString,x inx iny cpy Scratch blt fracLoop ; Set the length byte, and terminate the string with a '.' setLength MoveWord #$002E,X_NString:x ; hex for '.' txa dec a ora X_NString sta X_NString ; If the string came from SANE and is in Sci. Not. it is possible that ; there is a space in front of the number. This is all that the postSciNot ; code does, so call it. stripZeros jsr doPostSciNot ; If the number has trailing 0's after the decimal pt, strip them. lda [StringPtr] and #$00FF sta EndPos lda X_DecForm+2 cmp #1 beq doneShift ; Find the decimal. ldy FirstNumber genLoop1 iny lda [StringPtr],y and #$00FF cmp #'.' bne genLoop1 sty DotPos cpy EndPos ; is first dot the terminator? bge doneShift ; Find the end of fraction, terminated by 'e' or '.' genLoop2 iny lda [StringPtr],y and #$00FF cmp #'e' beq doneGenLoop2 cmp #'.' bne genLoop2 doneGenLoop2 sty EPos ; Go backwards as long as there are 0's genLoop3 dey lda [StringPtr],y and #$00FF cmp #'0' beq genLoop3 cpy DotPos beq doShift iny ; Remove'em, if any, and remove the decimal if no digits are still to right. doShift sty ShiftPos SubWord EPos,ShiftPos,Scratch beq doneShift lda X_NString tax and #$FF00 sta X_NString txa and #$00FF SubWord a,Scratch,EndPos ora X_NString sta X_NString ldx EPos genLoop4 lda X_NString,x sta X_NString,y cpy EndPos bge doneShift iny iny inx inx bra genLoop4 doneShift doPostFixed rts ;------------------------------------------ ; In Float format strings coming from SANE, there ; is sometimes a space at the beginning of the string. ; Remove it, if it is there. doPostSciNot lda X_NString tax and #$FF00 cmp #$2000 ; Space in high byte bne doneExpLoop txa and #$00FF sta EndPos ldy #0 expLoop iny lda X_NString,y xba sta X_NString,y cpy EndPos blt expLoop dec X_NString doneExpLoop rts ;------------------------------------------ doPostPercent ; Attach '%' to the end lda [StringPtr] and #$00FF inc a sta InsertPos MoveWord #'%',InsertChar jsr InsertCharacter rts ;------------------------------------------ doPostDollar ; Prefix '$' to the beginning MoveWord FirstNumber,InsertPos MoveWord #'$',InsertChar jsr InsertCharacter inc FirstNumber rts ;------------------------------------------ ;------------------------------------------ ; This mini-routine is used to fill the font ; width table, used by this routine. The hope ; is that X_FormatValue will get called with ; the same font many times in a row. FillTable stx X_CurFontID sta X_CurFontID+2 ; Remember the widths of '.', ',', ')', and '(' + ')'. Tool _CharWidth,in=(#$2E:w),out=(X_DotWidth:w) ; '.' Tool _CharWidth,in=(#',':w),out=(X_CommaWidth:w) Tool _CharWidth,in=(#')':w),out=(X_CParenWidth:w) Tool _CharWidth,in=(#'(':w),out=(a:w) AddWord a,X_CParenWidth,X_OParenWidth ; Use the max of '-' and '+' for the MinusWidth Tool _CharWidth,in=(#'-':w),out=(X_MinusWidth:w) Tool _CharWidth,in=(#'+':w),out=(a:w) cmp X_MinusWidth blt findMaxDigitWidth sta X_MinusWidth ; Find the widest of all the digits. 'e' counts as a digit for Sci. Not. purposes. findMaxDigitWidth stz X_MaxDigitWidth MoveWord #'9',Scratch sizeLoop Tool _CharWidth,in=(a:w),out=(a:w) cmp X_MaxDigitWidth blt chkEnd sta X_MaxDigitWidth chkEnd dec Scratch CmpWord Scratch,#'0' bge sizeLoop Tool _CharWidth,in=(#'e':w),out=(a:w) cmp X_MaxDigitWidth blt endFill sta X_MaxDigitWidth endFill rts X_CurFontID DC.L -1 ; hopefully no FontID of -1 exists! X_DotWidth DS.W 1 X_CommaWidth DS.W 1 X_MinusWidth DS.W 1 X_CParenWidth DS.W 1 X_OParenWidth DS.W 1 ; width of both parens; not use w/o ')' X_MaxDigitWidth DS.W 1 X_Sane0 DC.W 0,0,0,0,0 ;0 X_Sane10th DC.B $CD,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$FB,$3F ; 0.1 X_Sane1 DC.B 0,0,0,0,0,0,0,$80,$FF,$3F ;1 X_Sane10 DC.B 0,0,0,0,0,0,0,$A0,$02,$40 ;10 X_Sane100 DC.B 0,0,0,0,0,0,0,$C8,$05,$40 ;100 X_SaneINF DC.B 0,0,0,0,0,0,0,0,$FF,$7F ;infinity X_Float DS.X 1 X_Float2 DS.X 1 X_Float3 DS.X 1 ENDP ;-------------------------------------------------------------- ; X_MoveSaneX ; X_MoveSaneX PROC input FromPtr:l,ToPtr:l BEGIN MoveWord [FromPtr],[ToPtr] ldy #2 MoveWord [FromPtr]:y,[ToPtr]:y ldy #4 MoveWord [FromPtr]:y,[ToPtr]:y ldy #6 MoveWord [FromPtr]:y,[ToPtr]:y ldy #8 MoveWord [FromPtr]:y,[ToPtr]:y RETURN ENDP ;-------------------------------------------------------------- X_SANEData PROC EXPORT EXPORT X_decform EXPORT X_nstring EXPORT X_drec EXPORT X_ErrorStr EXPORT X_NAStr EXPORT X_CircStr EXPORT X_PoundStr X_decform DC.W 1,17 ; sgn, exp X_nstring DS.B 256 ; sig X_drec DS.B 260 X_ErrorStr STR 'Error' X_NAStr STR 'N/A' X_CircStr STR 'Circ.' X_PoundStr STR '##' ENDP END