This is an archived, read-only copy of the United-TI subforum , including posts and topic from May 2003 to April 2012. If you would like to discuss any of the topics in this forum, you can visit Cemetech's z80 & ez80 Assembly subforum. Some of these topics may also be directly-linked to active Cemetech topics. If you are a Cemetech member with a linked United-TI account, you can link United-TI topics here with your current Cemetech topics.

This forum is locked: you cannot post, reply to, or edit topics. Z80 & 68k Assembly => z80 & ez80 Assembly
Author Message
simplethinker
snjwffl


Active Member


Joined: 25 Jul 2006
Posts: 700

Posted: 02 Jan 2009 06:28:58 pm    Post subject:

Since I'm still a beginner with Asm most of my issues won't merit a whole new thread, so I figured I could just use this.

I'm working on a program that will take data stored in a string and create a matrix and I've run into a couple problems. The input string (for now) is Str0 and stores into [A]. The data will be two-digit hex numbers (i.e. "FF", "02"). Str0 will start with the number of rows followed by the number of columns followed by the data (so if Str0="02020B01FF22", then [A]=[[11,1][255,34]]). Here's what I have:

Code:
.nolist
   #include "ti83plus.inc"
.list
.org   $9D93
   .db   t2ByteTok, tAsmCmp


   ld   HL, Str0   ;initialize Str0
   b_call(_Mov9ToOP1)
   b_call(_ChkFindSym)
   ex   DE, HL   ;load length counter into BC
   ld   C, (HL)
   inc   HL
   ld   B, (HL)
   inc   HL
   ex   DE, HL
   srl   B   ;divide by 2
   rr   C
   dec   BC   ;skip the dimension bytes
   dec   BC
   push   BC   ;save length counter

   ex   DE, HL   ;put pointer to string in HL
   call   ByteToA   ;get number of rows
   push   AF   ;save it
   call   ByteToA
   pop   DE
   ld   E, A   ;get D=rows, E=columns

   push   HL   ;save string pointer
   push   DE   ;save dimensions
   ld   HL, MatA
   b_call(_Mov9ToOP1)
   b_call(_FindSym)   ;delete [A] if exists
   jr   C, _MOkay
   b_call(_DelVar)
_MOkay:
   ld   HL, MatA   ;create [A]
   b_call(_Mov9ToOP1)
   pop   HL
   b_call(_CreateRMat)   ;matrix pointer in DE
   inc   DE   ;matrix data starts 2 bytes in
   inc   DE
   pop   HL   ;get string pointer
   pop   BC   ;get length counter

_WriteLoop:
   push   BC
   push   DE   ;save matrix pointer
   call   ByteToFloat   ;FP number in OP1, HL + 2
   pop   DE   ;matrix pointer in DE
   push   HL   ;save string pointer
   b_call(_MovFrOP1)   ;data written and DE + 9
   pop   HL   ;get string pointer
   pop   BC
   dec   BC
   jr   NZ, _WriteLoop   ;iterate over whole matrix
   ret


;ByteToFloat
; In:   HL = pointer to string
; Out:   OP1 = FP value
;   HL + 2
; Destroys:   All, +OP2

ByteToFloat:
   call   ByteToA
   push   HL   ;save string pointer
   ld   H, 0
   ld   L, A   ;integer now in HL
   b_call(_SetXXXXOP2)   ;FP in OP2
   b_call(_OP2ToOP1)   ;now in OP1
   pop   HL
   ret

;ByteToA
; In:   HL = pointer to string
; Out:   A = integer value
;   HL + 2
; Destroys:   DE

ByteToA:
   call   NibbleToA   ;get high nibble
   add   A, A   ;shift left 16 bits
   add   A, A
   add   A, A
   add   A, A
   push   AF   ;save high nibble
   call   NibbleToA   ;low nibble
   pop   DE   ;high nibble in D
   add   A, D   ;total in A
   ret

;NibbleToA
; In:   HL = pointer to string
; Out:   A = Integer represented by string
;   HL + 1
; Destroys:   DE

NibbleToA:
   ld   DE, StrToHex - $30   ;number @ StrToHex - $30 + ASCII code
   ld   A, (HL)
   add   A, E
   jr   NC, _NoCarry
   inc   D
_NoCarry:
   ld   E, A
   ld   A, (DE)
   inc   HL
   ret


Str0:   .db   $04, tVarStrng, tStr0, $00
MatA:   .db   $02, tVarMat, tMatA, $00

StrToHex:   ; ASCII - $30
   .db   $00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $FF, $FF   ;used $FF as junk for the hell of it
   .db   $FF, $FF, $FF, $FF, $FF, $0A, $0B, $0C, $0D, $0E, $0F

.end
.end

note: In the loop BC is the number of elements still needing to be copied, DE points to the matrix data, and HL points to the string data.

It works fine until it runs into a zero or one. It copies the number correctly, but after that it gives junk. Sometimes it's all zeroes and other times it's random (it might be zeroes because I reset my RAM on the emulator every once in a while, so there's pretty much nothing in RAM).

The second problem is the speed. This is going to be used for up to 32x32 (1024 element) matrices, and even with a 5x5 matrix there's a delay. I'm guessing the slow speed is due to the 3 b_calls during each iteration (SetXXXXOP2, OP2ToOP1, and MovFrOP1) but I'm not sure. If that's the case, then I'm thinking I'll need to create a routine to convert an integer to floating-point and then I'll still need the MovFrOP1. Is there a better way to do it?

Thanks again for any help.
Back to top
WikiGuru
ADOS (Attention deficit... Oh! Shiny!)


Elite


Joined: 15 Sep 2005
Posts: 923

Posted: 07 Jan 2009 03:28:46 am    Post subject:

Hmm...

dec on 16-bit registers doesn't change the flags, and there are some problems with your byteToFloat/byteToA/nibbleToA (see code for changes). I've also added a few checks at the beginning to see if str0 exists, and if it's in the RAM. Of course, if it's not in RAM you could use UnArchive, but here I just quit.

Here's some code that does what you want:


Code:
   ld   HL, Str0   ;initialize Str0
   bcall(_Mov9ToOP1)
   bcall(_ChkFindSym)
   ret   c      ; not found, quit
   
   xor   A
   or   B
   ret   nz      ; not in RAM, quit
   
   ex   DE,HL      ;load length counter into BC
   ld   C, (HL)
   inc   HL
   ld   B, (HL)
   inc   HL
   
   ex   DE, HL
   srl   B      ;divide by 2
   rr   C
   dec   BC      ;skip the dimension bytes
   dec   BC
   push   BC      ;save length counter

   ex   DE, HL      ;put pointer to string in HL
   call   ByteToA   ;get number of rows
   push   AF      ;save it
   call   ByteToA   ;Get number of Cols
   pop   DE
   ld   E, A      ;get D=rows, E=columns
   
   push   HL      ;save string pointer
   push   DE      ;save dimensions
   ld   HL, MatA
   bcall(_Mov9ToOP1)
   bcall(_FindSym)   ;delete [A] if exists
   jr   C, _MOkay
   bcall(_DelVar)
_MOkay:
   ld   HL, MatA   ;create [A]
   bcall(_Mov9ToOP1)
   pop   HL
   bcall(_CreateRMat)   ;matrix pointer in DE
   inc   DE      ;matrix data starts 2 bytes in
   inc   DE
   pop   HL      ;get string pointer
   pop   BC      ;get length counter
   ld   A,C
   push   AF
   
_WriteLoop:
   push   BC
   push   DE      ;save matrix pointer
   call   ByteToFloat   ;FP number in OP1, HL + 2
   pop   DE      ;matrix pointer in DE
   push   HL      ;save string pointer
   bcall(_MovFrOP1)   ;data written and DE + 9
   pop   HL      ;get string pointer
   pop   BC
   dec   C
   jr   NZ,_WriteLoop
   pop   AF
   ld   C,A
   push   AF
   dec   B
   jr   NZ, _WriteLoop   ;iterate over whole matrix
   pop   AF
   ret


;ByteToFloat
; In:   HL = pointer to string
; Out:   OP1 = FP value
;   HL + 2
; Destroys:   All, +OP2

ByteToFloat:
   call   ByteToA
   push   HL   ;save string pointer
   
   ld h,$80

   sla   a
   sla   a
   sla   a
   sla   a
   
   CP   $A0   ; see if we need to change anything
   jr   c,f_ok

   sub   $0A   ; convert letter
   add   A,$10
   ld   h,$81
   
f_ok:
   ld (op1+2),a
   ld l,0
   ld (op1),hl
   ld h,0
   ld (op1+3),hl
   ld (op1+5),hl
   ld (op1+7),hl

   pop   HL
   ret

;ByteToA
; In:   HL = pointer to string
; Out:   A = integer value
;   HL + 2
; Destroys:   DE

ByteToA:
   call   NibbleToA   ;get high nibble
   sla   A
   sla   A
   sla   A
   sla   A
   push   AF

   call   NibbleToA   ;low nibble
   
   ld   D,A
   pop   AF
   add   A, D   ;total in A
   ret

;NibbleToA
; In:   HL = pointer to string
; Out:   A = Integer represented by string
;   HL + 1

NibbleToA:
   ld   A, (HL)
   inc   HL
   sub   $30
   cp   $10      ; see if a letter
   ret   c
   dec   A
   ret


Str0:   .db   $04, tVarStrng, tStr0, $00
MatA:   .db   $02, tVarMat, tMatA, $00


Last edited by Guest on 07 Jan 2009 03:32:28 am; edited 1 time in total
Back to top
calc84maniac


Elite


Joined: 22 Jan 2007
Posts: 770

Posted: 07 Jan 2009 10:22:02 am    Post subject:

I think your NibbleToA routine is messed up, too... it would return $10 for "A" instead of 10. ;)

I'll see if I can type up an insanely optimized one. :D

Edit:
Heh heh... I had an extremely optimized one for converting a value to ASCII, but not the other way around... I should think about this more...


Last edited by Guest on 07 Jan 2009 10:32:28 am; edited 1 time in total
Back to top
WikiGuru
ADOS (Attention deficit... Oh! Shiny!)


Elite


Joined: 15 Sep 2005
Posts: 923

Posted: 07 Jan 2009 11:58:10 am    Post subject:

Hehe, that's what I get for doing it at 2 in the morning... now that I'm hopefully awake, I have fixed NibbleToA, and am working on fixing byteToFloat.

edit: Well, here's the fixed nibbleToA. I still haven't figured out byteToFloat...


Code:
;NibbleToA
; In:   HL = pointer to string
; Out:   A = Integer represented by string
;   HL + 1

NibbleToA:
   ld   A, (HL)
   inc   HL
   sub   $30
   cp   $10   ; see if a letter
   ret   c
   sub 7
   ret


Last edited by Guest on 07 Jan 2009 12:50:42 pm; edited 1 time in total
Back to top
simplethinker
snjwffl


Active Member


Joined: 25 Jul 2006
Posts: 700

Posted: 07 Jan 2009 08:43:57 pm    Post subject:

Thanks for the replies. This is for a project I'm working on, so I didn't add any error checking since this will (probably) never be used by someone else, thus idiot-proofing it isn't needed (hopefully) :)

I've been working on the ByteToFloat part and I think I'm on the right track. The part after _LessThan100 I just threw together as a starting point (it only works for numbers between 100 and 255), so it's the gist of what I'm doing and will be drastically optimized and generalized for <100. Does this look like an efficient way to convert to a floating-point number (between 0-255) without any b_calls or is there another way?

Code:
.nolist
   #include "ti83plus.inc"
.list
.org   $9D93
   .db   t2ByteTok, tAsmCmp


   ld   HL, Str0
   b_call(_Mov9ToOP1)
   b_call(_ChkFindSym)   ;pointer to string in DE
   inc   DE   ;data starts 2 bytes in
   inc   DE
   call   ByteToA
   ld   H, 0   ;display integer
   ld   L, A
   b_call(_DispHL)
   ret

;ByteToFloat
; In:   DE = pointer to string
; Out:   (gFloat) has float
; Destroys:   BC, HL

ByteToFloat:
   push   DE
   call   ByteToA   ;A has the integer

;set exponent byte
   ld   B, 0   ;counts number of digits to be converted

   cp   $0A
   jr   c, _LessThan10
   ld   HL, $81
   ld   (gFloat + 1), HL
   inc   B
_LessThan10:

   cp   $64
   jr   c, _LessThan100
   ld   HL, $82
   ld   (gFloat + 1), HL
   inc   B
_LessThan100:


;--here's where it will be optimized/fixed/made to be not-sucky/etc...
   ld   H, 0   ;integer in HL
   ld   L, A
   ld   A, B
   cp   $02
   jr   NZ, _LessThan100_2   ;skip if int<100

   push   BC   ;save exponent
   ld   C, $64
   call   Div_HL_C   ;divide by 100, HL = quotient, A = remainder
   call   Lx16   ;shift into high nibble
   ld   D, L
   ld   L, A   ;put remainder in L

   ld   C, $0A
   call   Div_HL_C
   ld   E, A   ;save remainder in E
   ld   A, L   ;quotient in A
   add   A, D   ;total in A
   ld   (gFloat + 2), A   ;put total into float

   ld   L, E   ;remainder in L
   call   Lx16   ;shift into high position
   ld   A, L
   ld   (gFloat + 3), A   ;put ones-digit into float
_LessThan100_2:
        pop    BC

;more stuff will be going here

        pop    DE
   ret



;ByteToA
; In:   DE = pointer to string
; Out:   A = integer represented by string
;   DE + 1
; Destroys:   HL

ByteToA:
   call   NibbleToA   ;get high nibble
   sla   A   ;shift into high nibble
   sla   A
   sla   A
   sla   A
   push   AF   ;save integer
   call   NibbleToA
   pop   HL   ;high nibble now in H
   add   A, H   ;get total
   ret


;NibbleToA
; In:   DE = pointer to string
; Out:   A = integer represented by string
;   DE + 1
; Destroys:   HL

NibbleToA:
   ld   A, (DE)
   inc   DE
   sub   $30   ;ASCII code - $30
   cp   $0A   ;return if integer <$A
   ret   c
   sub   $07
   ret


;Div_HL_C
; In:   HL = dividend
;   C = divisor
; Out:   HL = HL / C
;   A = remainder
; Destroys:   B

Div_HL_C:
   xor   A   ;clear remainder
   ld   B, 16   ;dividend is 16-bits
_loop:
   add   HL, HL   ;shift left
   rla   ;carry to remainder
   jr   c, _overflow
   cp   C
   jr   c, _skip
_overflow:
   sub   C
   inc   L
_skip:
   djnz   _loop
   ret

;Lx16
; In:   L = integer
; Out:   L = L * 16
; Destroys:   none

Lx16:
   sla   L
   sla   L
   sla   L
   sla   L
   ret


Str0:   .db   $04, tVarStrng, tStr0, $00
gFloat:   .db   $00, $80, $FF, $F0, $00, $00, $00, $00, $00   ;F's are digits that can be modified

.end
.end


@WikiGuru: In your NibbleToA it should be "cp 10" and not "cp $10"


Now for the "Miscellaneous questions" part:
1) In Asm in 28 days' Z80 instruction set, after "operation" and "opcode" is "T-states". What are T-states?

2) Again about the Z80 instructions. I know that the names are equated to hex values, which are the actual machine code, but how does the machine actually execute them? Are they hard-coded into the circuitry itself, such that command $87 (for example) adds the accumulator to itself, or is there something else between the hex code and circuits?

3) For the opcodes for 'set' and 'res', it needs the binary form of [regindex], like IX or IY. Looking through the SDK 83+ developer guide I can't find what their binary forms are. Does anyone know where to find them?


[edit]calc84maniac: Do you still have the program that goes from an integer to ASCII? I just found the need for one in another program I'm working on.


Last edited by Guest on 07 Jan 2009 09:20:32 pm; edited 1 time in total
Back to top
WikiGuru
ADOS (Attention deficit... Oh! Shiny!)


Elite


Joined: 15 Sep 2005
Posts: 923

Posted: 07 Jan 2009 09:23:07 pm    Post subject:

I can't quite figure out what's wrong with ByteToFloat. Hopefully more debugging will work.

As for your questions:
1.
T-States can be thought of as a single operation the z80 CPU does. The number of T-States an instruction takes is how many operations the CPU actually does. The CPU's frequency is the number of T-States it can do every second (for TI-83+ Series, 6MHz or 11MHz on turbo mode).

2.
From a programming perspective, it's irrelevant. However, if you really want to know, see here: http://www.z80.info/zip/z80pps.zip. Inside is a little diagram of the CPU's workings while performing a few instructions. And yes, the OpCodes are hard-coded, and can not be changed.

3.
I'm not sure what you're talking about...
set imm3,reg8
imm3: a 3-bit immediate (number between 0 and 7)
reg8: Any of the following registers: A,B,C,D,E,H,L,IXH,IXL,IYH,IYL

same thing with res
Back to top
simplethinker
snjwffl


Active Member


Joined: 25 Jul 2006
Posts: 700

Posted: 07 Jan 2009 09:28:18 pm    Post subject:

2) Thanks Smile I was just wondering about the inner workings of it.

3) Res also has the syntax "SET imm3,(regindex + ofs8)" and the opcode is "[regindex] : 11001011 : [ofs8] : 11[imm3]110", and I'm not sure how to get [regindex].
Back to top
WikiGuru
ADOS (Attention deficit... Oh! Shiny!)


Elite


Joined: 15 Sep 2005
Posts: 923

Posted: 07 Jan 2009 09:31:58 pm    Post subject:

Oh yeah! I forgot about that! You can set an offset of IX/IY! Basically, you can add any 8-bit signed integer as such:


set 3,(IY+3)


This is how system flags works. It's actually changing bits in RAM.


;DonePrgm = 5
;DoneFlags = 0
res DonePrgm,(IY+DoneFlags)


Last edited by Guest on 11 Jul 2010 06:37:48 pm; edited 1 time in total
Back to top
simplethinker
snjwffl


Active Member


Joined: 25 Jul 2006
Posts: 700

Posted: 07 Jan 2009 09:34:29 pm    Post subject:

Yeah, that's it Smile My question is about the actual opcode though. The first part, which is [regindex] and is followed by : 11001011 : [ofs8] : 11[imm3]110

Last edited by Guest on 11 Jul 2010 06:38:03 pm; edited 1 time in total
Back to top
benryves


Active Member


Joined: 23 Feb 2006
Posts: 564

Posted: 07 Jan 2009 09:38:56 pm    Post subject:

Obligatory link to the Z80 User Manual. Smile
Back to top
simplethinker
snjwffl


Active Member


Joined: 25 Jul 2006
Posts: 700

Posted: 07 Jan 2009 09:44:41 pm    Post subject:

benryves wrote:
Obligatory link to the Z80 User Manual. Smile

Thanks! I have the 83+ system routines and SDK 83+ dev guide and I forgot there was a manual for the processor itself Very Happy
Back to top
FloppusMaximus


Advanced Member


Joined: 22 Aug 2008
Posts: 472

Posted: 07 Jan 2009 09:45:40 pm    Post subject:

T-states are the internal subdivisions of the Z80 operations. The simplest Z80 instructions have four T-states, roughly:
(1) fetch the opcode from memory;
(2) increment the program counter;
(3) perform the desired operation;
(4) memory refresh.
(I might have the order wrong.) In general, one T-state takes one clock cycle -- sometimes they can take two or more due to memory delays, especially on the 84+, but for most purposes, you can think of one T-state as equal to one six-millionth of a second.

More complicated instructions, such as those that read or write memory or I/O ports, and those that do 16-bit arithmetic, will require more T-states to execute. So if you're writing a piece of code that may need to be called thousands of times per second, it's useful to know how long each instruction will take to execute.

Internally, the Z80 basically does what you describe: first, it reads a byte from memory and stores it in an internal register. Next, the CPU's control unit "examines" the contents of that register and directs the rest of the CPU to perform the operation indicated. This logic is indeed pretty much "hard-coded" into the control unit, although if you consider the structure of the Z80 opcodes, the required circuitry isn't quite as complicated as you might think. For instance, every opcode 10xxxyyy is an 8-bit ALU instruction; the xxx determines which operation to perform, and the yyy determines which register to use as the input.

The bitwise operations on (IX+n) are similar to the corresponding operations on (HL), with the addition of the prefix DD and the displacement n. So the machine code for SET 0,(HL) is CB C6, while the code for SET 0,(IX+42h) is DD CB 42 C6 (the displacement is placed in the "middle" of the instruction for speed, if you can believe it -- if the 42 came after the C6, the instruction would probably require 3-4 additional T-states.)

edit: Duh, I see some other folks have already answered those questions. Hope this is still helpful Razz


Last edited by Guest on 07 Jan 2009 09:46:55 pm; edited 1 time in total
Back to top
simplethinker
snjwffl


Active Member


Joined: 25 Jul 2006
Posts: 700

Posted: 07 Jan 2009 10:02:37 pm    Post subject:

Thanks FloppusMaximus. So for set n, (IX+m) (or (IY+m)) (or for 'res') it's the same as for (HL) but with $DD (or $FD) in front and 'm' stuck in between. That's easy enough :)

[edit] For converting an integer to FP format, I remember Iambian once mentioned something about a "hack" using DAA, does anyone know what it is? If I understand correctly, DAA adjusts the accumulator already in BCD (binary coded decimal) for BCD arithmetic, so I'm not sure how it could be used to get a number into BCD format.


Last edited by Guest on 11 Jul 2010 06:38:20 pm; edited 1 time in total
Back to top
FloppusMaximus


Advanced Member


Joined: 22 Aug 2008
Posts: 472

Posted: 08 Jan 2009 12:47:30 am    Post subject:

I don't know what trick in particular you're referring to. The fastest way to convert an integer to BCD is, of course, a lookup table, but if you're looking for something more compact, DAA can certainly be helpful. Here's one way to do it:

Code:
   ld c,a
   xor a
   ld b,8
Loop:
   sla c
   adc a,a
   daa
   djnz Loop


I have, however, seen one piece of code that I would describe as a "DAA hack". This little piece of wizardry (I'm afraid I don't remember where I first saw this) has nothing to do with floating point or BCD:

Code:
   cp 0Ah
   ccf
   adc a,30h
   daa

I'll leave its purpose as an exercise for the reader.
Back to top
calc84maniac


Elite


Joined: 22 Jan 2007
Posts: 770

Posted: 08 Jan 2009 02:18:54 am    Post subject:

simplethinker wrote:
[edit]calc84maniac: Do you still have the program that goes from an integer to ASCII? I just found the need for one in another program I'm working on.

Indeed.

Code:
cp 10
sbc a,$69
daa
Back to top
WikiGuru
ADOS (Attention deficit... Oh! Shiny!)


Elite


Joined: 15 Sep 2005
Posts: 923

Posted: 08 Jan 2009 12:54:24 pm    Post subject:

Yay, hopefully this will work. It's extremely un-optimized.

Code:

   ld   HL, Str0   ;initialize Str0
   bcall(_Mov9ToOP1)
   bcall(_ChkFindSym)
   ret   c      ; not found, quit

   xor   A
   or   B
   ret   nz      ; not in RAM, quit
   
   ex   DE,HL      ;load length counter into BC
   ld   C, (HL)
   inc   HL
   ld   B, (HL)
   inc   HL
   
   ex   DE, HL
   srl   B      ;divide by 2
   rr   C
   dec   BC      ;skip the dimension bytes
   dec   BC
   push   BC      ;save length counter

   ex   DE, HL      ;put pointer to string in HL
   call   ByteToA   ;get number of rows
   push   AF      ;save it
   call   ByteToA   ;Get number of Cols
   pop   DE
   ld   E, A      ;get D=rows, E=columns
   
   push   HL      ;save string pointer
   push   DE      ;save dimensions
   ld   HL, MatA
   bcall(_Mov9ToOP1)
   bcall(_FindSym)   ;delete [A] if exists
   jr   C, _MOkay
   bcall(_DelVar)
_MOkay:
   ld   HL, MatA   ;create [A]
   bcall(_Mov9ToOP1)
   pop   HL
   bcall(_CreateRMat)   ;matrix pointer in DE
   inc   DE      ;matrix data starts 2 bytes in
   inc   DE
   pop   HL      ;get string pointer
   pop   BC      ;get length counter
   ld   A,C
   push   AF
   
_WriteLoop:
   push   BC
   push   DE      ;save matrix pointer
   call   ByteToFloat   ;FP number in OP1, HL + 2
   pop   DE      ;matrix pointer in DE
   push   HL      ;save string pointer
   bcall(_MovFrOP1)   ;data written and DE + 9
   pop   HL      ;get string pointer
   pop   BC
   dec   C
   jr   NZ,_WriteLoop
   pop   AF
   ld   C,A
   push   AF
   dec   B
   jr   NZ, _WriteLoop   ;iterate over whole matrix
   pop   AF
   ret


;ByteToFloat
; In:   HL = pointer to string
; Out:   OP1 = FP value
;   HL + 2
; Destroys:   All

ByteToFloat:
   call   ByteToA
   push   HL
   ld   HL,$8000

   cp   $0A
   jr   c,intoOp1      ;under 10, no fixing needed
   
   inc   H
   
getHundreds:            ;get A>100, keep track in L
   cp   $64
   jr   c,under100
   ld   H,$82
   inc   L
   sub   $64
   jr   getHundreds
   
under100:
   ld   C,A         ;save both nibbles into C
   and   $F0         ;multiply upper nibble by $16, use daa
   sra   A
   sra   A
   sra   A
   sra   A
   ld   B,A
   or   A
   jr   z,skipMult      ;upper nibble was 0
   
   xor   A

fMult:
   add   A,$16
   daa
   djnz   fMult   

skipMult
   ld   B,A
   ld   A,C         ;retrieve lower nibble
   and   $0F
   add   A,B
   daa            ;fix
   
intoOp1:
   ld   (Op1),hl
   ld   B,A         ;save units/tens digits
   
   xor   A         ;zero out parts of Op1
   ld   (Op1),A
   ld   (Op1+3),A
   ld   (Op1+4),A
   ld   (Op1+5),A
   ld   (Op1+6),A
   ld   (Op1+7),A
   ld   (Op1+8),A
   
   ld   A,H
   cp   $81
   jr   c,oneDigit      ;10>x
   jr   z,twoDigits      ;10<x<100
   
threeDigits:            ;100<x
   sla   L         ;100's digit into upper nibble
   sla   L
   sla   L
   sla   L
   ld   A,B
   and   $F0         ;get 10's digit
   sra   A
   sra   A
   sra   A
   sra   A
   add   A,L
   ld   (Op1+2),A
   
   ld   A,B
   sla   A
   sla   A
   sla   A
   sla   A
   ld   (Op1+3),A
   jr   fDone

oneDigit:
   ld   A,B
   sla   A
   sla   A
   sla   A
   sla   A
   ld   (Op1+2),A
   jr   fDone
      
twoDigits:
   ld   A,B
   ld   (Op1+2),A

fDone:
   pop   HL
   ret
   
;ByteToA
; In:   HL = pointer to string
; Out:   A = integer value
;   HL + 2
; Destroys:   D

ByteToA:
   call   NibbleToA   ;get high nibble
   sla   A
   sla   A
   sla   A
   sla   A
   push   AF
   call   NibbleToA   ;low nibble
   
   ld   D,A
   pop   AF
   add   A, D   ;total in A
   ret

;NibbleToA
; In:   HL = pointer to string
; Out:   A = Integer represented by string
;   HL + 1

NibbleToA:
   ld   A, (HL)
   inc   HL
   sub   $30
   cp   10      ; see if a letter
   ret   c
   sub 7
   ret


Str0:   .db   $04, tVarStrng, tStr0, $00
MatA:   .db   $02, tVarMat, tMatA, $00
Back to top
simplethinker
snjwffl


Active Member


Joined: 25 Jul 2006
Posts: 700

Posted: 09 Jan 2009 08:12:13 pm    Post subject:

It's alive! I finally got a byteToFloat routine working:

Code:
.nolist
   #include "ti83plus.inc"
.list
.org   $9D93
   .db   t2ByteTok, tAsmCmp


   ld   HL, Str0
   b_call(_Mov9ToOP1)
   b_call(_ChkFindSym)   ;pointer to string in DE
   inc   DE   ;data starts 2 bytes in
   inc   DE
   call   ByteToFloat   ;gFloat has FP number
   ld   HL, gFloat
   b_call(_Mov9ToOP1)   ;put float into OP1
   b_call(_ConvOP1)   ;convert to integer, stored in DE
   ex   DE, HL
   b_call(_DispHL)
   ret

;ByteToFloat
; In:   DE = pointer to string
; Out:   (gFloat) has float
; Destroys:   BC, HL

ByteToFloat:
   ld   HL, 0
   ld   (gFloat + 2), HL   ;zero-out gFloat

   call   ByteToA   ;A has the integer
   push   DE

;set exponent byte
   ld   B, 1   ;counts number of digits to be converted

   cp   $0A
   jr   c, _LessThan10
   ld   HL, $81
   inc   B
_LessThan10:

   cp   $64
   jr   c, _LessThan100
   ld   HL, $82
   inc   B
_LessThan100:
   ld   (gFloat + 1), HL   ;set exponent byte


   ld   C, 0   ;current nibble
_Loop:
   ld   D, A   ;save A
   ld   HL, baseTen - 1   ;get current power of ten into H
   ld   A, L
   add   A, B
   jr   NC, _NoCarry   ;increment H if need be
   inc   H
_NoCarry:
   ld   L, A
   ld   H, (HL)
   ld   L, D

   call   Div_L_H   ;L has quotient, A has remainder
   ld   D, L

   bit   0, C
   jr   NZ, _lowNibble   ;if 0-bit of C is 1, then it's odd, thus skip
   sla   D   ;shift into high nibble
   sla   D
   sla   D
   sla   D
_lowNibble:
   ld   HL, gFloat + 2
   bit   1, C   ;current byte
   jr   Z, _firstByte
   ld   HL, gFloat + 3
_firstByte:
   ld   E, A   ;save A
   ld   A, (HL)
   add   A, D   ;calculate total in byte
   ld   (HL), A   ;put back
   inc   C
   ld   A, E
   djnz   _Loop   ;repeat for other nibbles

   pop   DE   ;get DE back
   ret



;ByteToA
; In:   DE = pointer to string
; Out:   A = integer represented by string
;   DE + 1
; Destroys:   HL

ByteToA:
   call   NibbleToA   ;get high nibble
   add   A, A   ;shift into high nibble
   add   A, A
   add   A, A
   add   A, A
   push   AF   ;save integer
   call   NibbleToA
   pop   HL   ;high nibble now in H
   add   A, H   ;get total
   ret


;NibbleToA
; In:   DE = pointer to string
; Out:   A = integer represented by string
;   DE + 1
; Destroys:   HL

NibbleToA:
   ld   A, (DE)
   inc   DE
   sub   $30   ;ASCII code - $30
   cp   $0A   ;return if integer <$A
   ret   c
   sub   $07
   ret


;Div_L_H
; In:   L = dividend
;   H = divisor
; Out:   L = L / H
;   A = remainder
; Destroys:   D

Div_L_H:
   ld   D, B   ;save B
   xor   A   ;clear remainder
   ld   B, 8   ;dividend is 8 bits
_dLoop:
   sla   L   ;shift left
   rlA   ;carry to remainder
   jr   c, _overflow
   cp   H
   jr   c, _skip
_overflow:
   sub   H
   inc   L
_skip:
   djnz   _dLoop
   ld   B, D
   ret


Str0:   .db   $04, tVarStrng, tStr0, $00
gFloat:   .db   $00, $80, $FF, $F0, $00, $00, $00, $00, $00   ;blank floating-point number
baseTen:   .db   $01, $0A, $64

.end
.end


It works, but I'm not sure of its speed compared to the original version since my initial attempt at plugging it into the toMatrix program didn't work, and I don't want to push my luck tonight so I'll tinker with that part later Neutral . I think that with some more tweaking I can get it to work for an arbitrary number of digits, but again, I won't push my luck tonight.


On a side note, I've been having some trouble with Wabbit. Every once in a while, for no reason it starts bugging-out and showing and freezing on the Vars menu whenever I hit a button on the keyboard (when I use the mouse to click on the buttons nothing happens). Sometimes I can just quit and reenter the program, other times I need to quit and wait awhile. I've tried resetting the memory and even getting another ROM, but it still keeps happening.


Last edited by Guest on 10 Jan 2009 12:50:36 pm; edited 1 time in total
Back to top
simplethinker
snjwffl


Active Member


Joined: 25 Jul 2006
Posts: 700

Posted: 23 Jan 2009 08:36:13 pm    Post subject:

I finally have a decent String->Float routine working, but I'm still running into the problem of certain values not being stored into the matrix properly.

The String->Matrix program:

Code:
.nolist
   #include "ti83plus.inc"
.list
.org   $9D93
   .db   t2ByteTok, tAsmCmp


   ld   HL, Str0   ;initialize Str0
   b_call(_Mov9ToOP1)
   b_call(_ChkFindSym)
   ex   DE, HL   ;load length counter into BC
   ld   C, (HL)
   inc   HL
   ld   B, (HL)
   inc   HL
   ex   DE, HL   ;DE = string pointer
   srl   B   ;divide by 2
   rr   C
   dec   BC   ;skip the dimension bytes
   dec   BC
   push   BC   ;save length counter

   call   ByteToA   ;number of rows
   push   AF   ;save
   call   ByteToA   ;number of columns in A
   pop   HL   ;rows in H
   ld   L, A   ;columns in L

   push   DE   ;save string pointer
   push   HL   ;save dimensions
_MatInit:
   ld   HL, MatA
   b_call(_Mov9ToOP1)
   b_call(_ChkFindSym)   ;delete [A] if exists
   jr   c, _noMat
   b_call(_DelVar)
   jr   _MatInit
_noMat:
   pop   HL
   b_call(_CreateRMat)   ;matrix pointer in DE

   inc   DE   ;matrix data starts 2 bytes in
   inc   DE
   pop   HL   ;get string pointer
   ex   DE, HL   ;DE = string pointer, HL = matrix pointer
   pop   BC   ;get length counter

_WriteLoop:
   push   BC

   push   HL   ;save matrix pointer
   call   ByteToFloat   ;gFloat has FP number, DE + 2
   pop   HL   ;get matrix pointer
   call   FromGFloat   ;copy to matrix, HL + 9

   pop   BC
   dec   BC
   jr   NZ, _WriteLoop   ;iterate over whole matrix
   ret


;ByteToFloat
; In:   DE = pointer to string data
; Out:   gFloat = FP number
;   DE + 2
; Destroys:   All but DE
ByteToFloat:
   call   ByteToA
   push   DE   ;save string pointer

   ;get 1 + max power of 10 to E
   ld   E, 1   ;there's obviously at least a 1's digit

   ld   HL, 0
   ld   (nibbles + 1), HL   ;clear last two bytes of 'nibbles'
   ld   (gFloat + 2), HL   ;clear last two bytes of gFloat

   ld   HL, gFloat + 1
   ld   (HL), $80   ;set exponent byte
   cp   $64   ;increment E every time A>=10^N
   jr   c, _digitOne
   inc   E
   inc   (HL)   ;increment exponent byte
_digitOne:
   cp   $0A
   jr   c, _digitTwo
   inc   E
   inc   (HL)
_digitTwo:

   ld   HL, nibbles - 1   ;get first location to place digit
   ld   D, 0
   add   HL, DE      ;HL has location to place first digit

_iToFLoop:
   ld   C, A   ;- divide A by 10
   xor   A
   ld   B, 8
_divLoop:
   sla   C
   rlA
   jr   c, _divOverflow
   cp   $0A
   jr   c, _divSkip
_divOverflow:
   sub   $0A
   inc   C
_divSkip:
   djnz   _divLoop   ;now A has new digit, C has quotient

   ld   (HL), A      ;put digit into 'nibbles'
   ld   A, C      ;move quotient to A
   dec   HL      ;move to the left in 'nibbles'
   dec   E
   jr   nz, _iToFLoop

   ld   A, (nibbles)   ;get first digit
   add   A, A   ;shift into high nibble
   add   A, A
   add   A, A
   add   A, A
   ld   HL, nibbles + 1   ;move right
   add   A, (HL)   ;add low nibble
   ld   (gFloat + 2), A   ;save it

   ld   A, (nibbles + 2)   ;third digit
   add   A, A
   add   A, A
   add   A, A
   add   A, A
   ld   (gFloat + 3), A
   pop   DE
   ret


ByteToA:
   call   NibbleToA   ;get high nibble
   add   A, A   ;shift into high nibble
   add   A, A
   add   A, A
   add   A, A
   push   AF   ;save integer
   call   NibbleToA
   pop   HL   ;high nibble now in H
   add   A, H   ;get total
   ret

;NibbleToA
; In:   DE = pointer to string
; Out:   A = integer represented by string
;   DE + 1
; Destroys:   HL
NibbleToA:
   ld   A, (DE)
   inc   DE
   sub   $30   ;ASCII code - $30
   cp   $0A   ;return if integer <$A
   ret   c
   sub   $07
   ret


;FromGFloat
; In:   HL = destination
; Out:   HL + 9
; Destroys:   A
FromGFloat:
   ld   (HL), 0   ;copy sign byte
   inc   HL
   ld   A, (gFloat + 1)   ;copy exponent byte
   ld   (HL), A
   inc   HL

   ld   A, (gFloat + 2)   ;copy significand
   ld   (HL), A
   inc   HL
   ld   A, (gFloat + 3)
   ld   (HL), A
   inc   HL

   ld   (HL), 0   ;last 5 bytes 0
   inc   HL
   ld   (HL), 0
   inc   HL
   ld   (HL), 0
   inc   HL
   ld   (HL), 0
   inc   HL
   ld   (HL), 0
   inc   HL
   ret



Str0:   .db   $04, tVarStrng, tStr0, $00
MatA:   .db   $02, tVarMat, tMatA, $00
gFloat:   .db   $00, $80, $00, $00
nibbles:   .db   $00, $00, $00

.end
.end

It works fine for any numbers ≥101. With numbers <101 it stores it correctly but every element after that is junk (still). I don't think it's the ByteToFloat routine.

Code:
.nolist
   #include "ti83plus.inc"
.list
.org   $9D93
   .db   t2byteTok, tAsmCmp



   ld   HL, MatA   ;create [A]
   b_call(_Mov9ToOP1)
   ld   HL, $0102
   b_call(_CreateRMat)   ;pointer to data in DE
   inc   DE
   inc   DE
   push   DE   ;save matrix pointer

   ld   DE, String
   call   ByteToFloat   ;FP number in gFloat, DE + 2
   pop   HL   ;matrix pointer in HL
   call   FromGFloat
   push   HL   ;save matrix pointer
   call   ByteToFloat
   pop   HL
   call   FromGFloat
   ret


;ByteToFloat
; In:   DE = pointer to string data
; Out:   gFloat = FP number
;   DE + 2
; Destroys:   All but DE
ByteToFloat:
   call   ByteToA
   push   DE   ;save string pointer

   ;get 1 + max power of 10 to E
   ld   E, 1   ;there's obviously at least a 1's digit
   ld   HL, gFloat + 1
   ld   (HL), $80   ;set exponent byte
   cp   $64   ;increment E every time A>=10^N
   jr   c, _digitOne
   inc   E
   inc   (HL)   ;increment exponent byte
_digitOne:
   cp   $0A
   jr   c, _digitTwo
   inc   E
   inc   (HL)
_digitTwo:

   ld   HL, nibbles - 1   ;get first location to place digit
   ld   D, 0
   add   HL, DE      ;HL has location to place first digit

_iToFLoop:
   ld   C, A   ;- divide A by 10
   xor   A
   ld   B, 8
_divLoop:
   sla   C
   rlA
   jr   c, _divOverflow
   cp   $0A
   jr   c, _divSkip
_divOverflow:
   sub   $0A
   inc   C
_divSkip:
   djnz   _divLoop   ;now A has new digit, C has quotient

   ld   (HL), A      ;put digit into 'nibbles'
   ld   A, C      ;move quotient to A
   dec   HL      ;move to the left in 'nibbles'
   dec   E
   jr   nz, _iToFLoop

   ld   A, (nibbles)   ;get first digit
   add   A, A   ;shift into high nibble
   add   A, A
   add   A, A
   add   A, A
   ld   HL, nibbles + 1   ;move right
   add   A, (HL)   ;add low nibble
   ld   (gFloat + 2), A   ;save it

   ld   A, (nibbles + 2)   ;third digit
   add   A, A
   add   A, A
   add   A, A
   add   A, A
   ld   (gFloat + 3), A
   pop   DE
   ret


ByteToA:
   call   NibbleToA   ;get high nibble
   add   A, A   ;shift into high nibble
   add   A, A
   add   A, A
   add   A, A
   push   AF   ;save integer
   call   NibbleToA
   pop   HL   ;high nibble now in H
   add   A, H   ;get total
   ret

;NibbleToA
; In:   DE = pointer to string
; Out:   A = integer represented by string
;   DE + 1
; Destroys:   HL
NibbleToA:
   ld   A, (DE)
   inc   DE
   sub   $30   ;ASCII code - $30
   cp   $0A   ;return if integer <$A
   ret   c
   sub   $07
   ret

;FromGFloat
; In:   HL = destination
; Out:   HL + 9
; Destroy:   A
FromGFloat:
   ld   (HL), 0   ;copy sign byte
   inc   HL
   ld   A, (gFloat + 1)   ;copy exponent byte
   ld   (HL), A
   inc   HL

   ld   A, (gFloat + 2)   ;copy significand
   ld   (HL), A
   inc   HL
   ld   A, (gFloat + 3)
   ld   (HL), A
   inc   HL

   ld   (HL), 0   ;last 5 bytes 0
   inc   HL
   ld   (HL), 0
   inc   HL
   ld   (HL), 0
   inc   HL
   ld   (HL), 0
   inc   HL
   ld   (HL), 0
   inc   HL
   ret



gFloat:   .db   $00, $80, $00, $00, $00, $00, $00, $00, $00
nibbles:   .db   $00, $00, $00

String:   .db   $30, $31, $46, $45, $46, $44, $46, $43
MatA:   .db   $02, tVarMat, tMatA, $00


.end
.end

(note1: you have to manually delete [A] before running this one, I was too lazy to add in a check for [A] already existing)
(note2: the string is hardcoded into this program. The string is stored after the "String:" label. Dimensions can be set by altering the immediate value in "ld HL, $0102")

This program uses the exact same ByteToFloat routine and the exact same method of storing ByteToFloat's output to a matrix. It works fine for any input I've put in, so I'm pretty sure it's not either of those. That just leaves (I think) the rest of the code in "_WriteLoop", but I really don't know what part of that it could possibly be.

I'm completely stumped on this, and I would appreciate any suggestions.


More "miscellaneous questions"
1) In the "TASM80.TAB" file (the one that lists all the instructions and their hex equivalences), each line ends with a "1 NOP 1" or something similar (or a ZIX or ZBIT). What's the NOP/ZIX/ZBIT for (just the hex number is enough for when I compile by hand, so I'm not sure why they're there)? (it says look in the TASM manual for table structure but I can't find anything in any of the text files that came with TASM)

[s]2) How can you find where the code for a b_call is? I'm pretty sure it's not simply the b_call address. one reason I'm sure of this is that for the LdHLInd routine, it's b_call address is $4009, but the code for it is located at 0033h (I found this at wikiti).[/s] [edit 1/24] I found the answer in the TI-83+ SDK guide

Here's the source and compiled .8xp files [attachment=2576:StrToFloat.zip]


[edit] Now I feel dumb. I was rereading the previous posts when I noticed "dec on 16-bit registers doesn't change the flags" as the very first line of the very first response to my very first question Neutral I think my problem now boils down to finding a way to check whether C is non-zero that doesn't add too many clock cycles.

[another edit] I just stuck a inc C \ dec C right before the jr nz, _WriteLoop and it works fine. Wow, now I feel reeaaalllly dumb.
[another another edit] That fix shouldn't (but I didn't get any problems for some reason) work since C can be zero when B isn't, but dec C \ jr nz, _WriteLoop \ dec B \ jr nz, _WriteLoop replacing the dec BC and jump should do the trick.


Last edited by Guest on 11 Jul 2010 06:37:07 pm; edited 1 time in total
Back to top
FloppusMaximus


Advanced Member


Joined: 22 Aug 2008
Posts: 472

Posted: 24 Jan 2009 05:42:00 pm    Post subject:

You presumably want OR C, not ADD A,C; the latter will give a result of zero if BC equals (for instance) 01FFh.
Back to top
simplethinker
snjwffl


Active Member


Joined: 25 Jul 2006
Posts: 700

Posted: 24 Jan 2009 05:58:04 pm    Post subject:

FloppusMaximus wrote:
You presumably want OR C, not ADD A,C; the latter will give a result of zero if BC equals (for instance) 01FFh.

Thanks, I caught it just as you posted that, then came up with something that works but it was still longer than yours. Thanks! Very Happy
Back to top
Display posts from previous:   
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
    » Goto page 1, 2, 3  Next
» View previous topic :: View next topic  
Page 1 of 3 » All times are UTC - 5 Hours

 

Advertisement