antoine-source/appleworksgs/Scrap/Src/ssstring.aii
2023-03-04 03:45:20 +01:00

1 line
18 KiB
Plaintext
Executable File

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