6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 10:19 am

All times are UTC




Post new topic Reply to topic  [ 62 posts ]  Go to page Previous  1, 2, 3, 4, 5  Next
Author Message
PostPosted: Fri Mar 26, 2021 9:00 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
IamRob wrote:
I did. The outcome is still the same as your code above, but without the Scratch variable.

The AND #$10 preserves the Aux Carry flag, which comes from the DEX TXA EOR above.
The AND #$3A also preserves the Aux Carry flag. If you punch the code in you will see you get exactly the same result as when using the scratch variable.

I had a typo in my previous post. It should have been #$3A, not #$3C.

Consider
Quote:
Command? u100l4
0100 3E10 mvi A,10
0102 3D dcr A
0103 3D dcr A
Command? r
A=00 BC=0000 DE=0000 HL=0000 SP=0000 PC=0100 szapc #=0000000000000000
0100 3E10 mvi A,10
Command? t
A=10 BC=0000 DE=0000 HL=0000 SP=0000 PC=0102 szapc #=0000000000000007
0102 3D dcr A
Command? t
A=0F BC=0000 DE=0000 HL=0000 SP=0000 PC=0103 szAPc #=000000000000000C
0103 3D dcr A
Command? t
A=0E BC=0000 DE=0000 HL=0000 SP=0000 PC=0104 szapc #=0000000000000011
0104 C30000 jmp 0000


I do not see how the aux carry flag is cleared in the second dcr unless it is ANDed out before ORing in the new aux carry.


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 27, 2021 4:07 pm 
Offline
User avatar

Joined: Sun Nov 01, 2020 10:36 am
Posts: 35
Location: Tatooine
if you have some bits in A that you want to merge with other bits from a byte in memory you can do:

Code:
   eor Mem
   and #mask    ; mask corresponding to the bits you want from A; the others will be from Mem
   eor Mem


thus you can spare a STA
Code:
   ldx regA
   txa         ; A= old value
   dex
   stx regA
   eor regA      ; oldval Eor newval

   eor regCC         ; these lines insert the bits identified by maskAC
   and #maskAC         ; into the value from regCC
   eor regCC

   and #maskAC^maskC  ; only AC and C bits; zero the others ($10 OR $2A)

   ora ValueFlags,X
   sta regCC


As for "dcr M"
Code:
   clc
   lda RegL
   sta Ptr
   lda RegH
   adc #>Ram80
   sta Ptr+1
   ldy #0
   lda (Ptr),y
   tax      ; X=oldvalue

   ; clc      ; you don't need it: actually if you had Carry=1
         ; it means trouble because you wrapped around memory
   sbc #0      ; A=A-1
   sta (Ptr),y
   txa      ; A=oldvalue
   eor (Ptr),y
   
   eor regCC
   and #maskAC
   eor regCC

   and #maskAC^maskC

   ora ValueFlags,x
   sta regCC


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 27, 2021 5:27 pm 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
BillG wrote:
IamRob wrote:
I did. The outcome is still the same as your code above, but without the Scratch variable.

The AND #$10 preserves the Aux Carry flag, which comes from the DEX TXA EOR above.
The AND #$3A also preserves the Aux Carry flag. If you punch the code in you will see you get exactly the same result as when using the scratch variable.

I had a typo in my previous post. It should have been #$3A, not #$3C.

I do not see how the aux carry flag is cleared in the second dcr unless it is ANDed out before ORing in the new aux carry.

I see what I missed. You have COMMON and MCOMMON labels being called from somewhere else. I would need to see the circumstances in which those two labels are being called to be of any help.


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 28, 2021 5:22 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
BB8 wrote:
if you have some bits in A that you want to merge with other bits from a byte in memory you can do:

Code:
   eor Mem
   and #mask    ; mask corresponding to the bits you want from A; the others will be from Mem
   eor Mem
The force is strong in that one...

I am trying to wrap my head around it.

So if the bit in the accumulator is 0, the first eor replaces it with the corresponding one from Mem. The and preserves it. The second eor clears it.

If the bit is 1, the first eor replaces it with the inverse of the corresponding one from Mem. The second eor leaves it set.

OK.

That would replace this
Code:
 0004 29 10           [2] 00013          and    #f_A
 0006 85 02           [3] 00014          sta    Scratch
                          00015
 0008 A5 03           [3] 00016          lda    Reg_CC
 000A 29 2A           [2] 00017          and    #!(f_S|f_Z|f_A|f_P|f_C)
                          00018
 000C 05 02           [3] 00019          ora    Scratch
with this
Code:
 000E 45 03           [3] 00023          eor    Reg_CC
 0010 29 10           [2] 00024          and    #f_A
 0012 45 03           [3] 00025          eor    Reg_CC
                          00026
 0014 29 11           [2] 00027          and    #f_A|f_C
for a savings of two bytes and three cycles.

And this
Code:
 0016 B1 00         [5/6] 00031          lda    (Ptr),Y
 0018 AA              [2] 00032          tax
 0019 CA              [2] 00033          dex
 001A 8A              [2] 00034          txa
 001B 51 00         [5/6] 00035          eor    (Ptr),Y
 001D 29 10           [2] 00036          and    #f_A
 001F 85 02           [3] 00037          sta    Scratch
 0021 8A              [2] 00038          txa
 0022 91 00           [6] 00039          sta    (Ptr),Y
                          00040
 0024 A5 03           [3] 00041          lda    Reg_CC
 0026 29 2A           [2] 00042          and    #!(f_S|f_Z|f_A|f_P|f_C)
 0028 05 02           [3] 00043          ora    Scratch
with this
Code:
 002A B1 00         [5/6] 00047          lda    (Ptr),Y
 002C AA              [2] 00048          tax
 002D E9 00           [2] 00049          sbc    #0
 002F 91 00           [6] 00050          sta    (Ptr),Y
 0031 8A              [2] 00051          txa
 0032 51 00         [5/6] 00052          eor    (Ptr),Y
                          00053
 0034 45 03           [3] 00054          eor    Reg_CC
 0036 29 10           [2] 00055          and    #f_A
 0038 45 03           [3] 00056          eor    Reg_CC
                          00057
 003A 29 11           [2] 00058          and    #f_A|f_C
for a savings of two bytes and five cycles.


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 28, 2021 5:36 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
IamRob wrote:
You have COMMON and MCOMMON labels being called from somewhere else. I would need to see the circumstances in which those two labels are being called to be of any help.

There are fourteen varieties of dcr and inr register instructions.

One of the others is:
Code:
                          02543 ;
                          02544 ; dcr B ; Decrement register B
                          02545 ;
 B17C                     02546 dcrB_:
 B17C A6 17           [3] 02547          ldx    Reg_B     ; Decrement the register
 B17E CA              [2] 02548          dex
                          02549
 B17F 8A              [2] 02550          txa              ; Make a copy of the result
 B180 45 17           [3] 02551          eor    Reg_B     ; Borrowed from upper nybble if its low bit changed
                          02552
 B182 86 17           [3] 02553          stx    Reg_B     ; Store the new value
                          02554
 B184 4C B16A         [3] 02555          jmp    dcrCommon


In addition to dcr M, there is the similar inr M; both end up jumping to dcrMCommon


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 28, 2021 5:48 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
In the meantime, I worked on the daa instruction, decimal adjust accumulator. This is probably the most complicated instruction on the 8080.

The programming manual for the 6800 provides the most detailed explanation I have seen:

Attachment:
daa.png
daa.png [ 232.31 KiB | Viewed 867 times ]


This is the code:
Code:
                          02374 ;
                          02375 ; daa ; Decimal adjust accumulator
                          02376 ;
                          02377 ; See page A-34 in the Motorola M6800 Programming Reference Manual
                          02378 ;
 B0C8                     02379 daa__:
 B0C8 A5 15           [3] 02380          lda    Reg_A
 B0CA A8              [2] 02381          tay              ; Make a copy of the high nybble
 B0CB 29 0F           [2] 02382          and    #$F       ; Isolate low nybble
 B0CD AA              [2] 02383          tax
                          02384
 B0CE E0 0A           [2] 02385          cpx    #9+1      ; Is low nybble above 9?
 B0D0 90 06 (B0D8)  [2/3] 02386          blo    daaLowInRange ; No
                          02387
 B0D2 C0 90           [2] 02388          cpy    #$90      ; Will this cause upper nybble to go over?
 B0D4 B0 10 (B0E6)  [2/3] 02389          bhs    daaAdjustUpper ; Yes
 B0D6 90 08 (B0E0)  [2/3] 02390          blo    daaNotAux
                          02391
 B0D8                     02392 daaLowInRange:
 B0D8 A5 14           [3] 02393          lda    Reg_CC
 B0DA 29 10           [2] 02394          and    #f_A      ; Is aux carry flag set?
 B0DC F0 02 (B0E0)  [2/3] 02395          beq    daaNotAux ; No
                          02396
 B0DE A2 0A           [2] 02397          ldx    #10       ; Lower nybble needs to be adjusted
                          02398
 B0E0                     02399 daaNotAux:
 B0E0 A5 14           [3] 02400          lda    Reg_CC
 B0E2 29 01           [2] 02401          and    #f_C      ; Is carry flag set?
 B0E4 F0 02 (B0E8)  [2/3] 02402          beq    daaProceed ; No
                          02403
 B0E6                     02404 daaAdjustUpper:
 B0E6 A0 A0           [2] 02405          ldy    #$A0      ; Upper nybble needs to be adjusted
                          02406
 B0E8                     02407 daaProceed:
 B0E8 A9 00           [2] 02408          lda    #0        ; Start with no adjustments
                          02409
 B0EA E0 0A           [2] 02410          cpx    #10       ; Does the lower nybble need adjustment?
 B0EC 90 02 (B0F0)  [2/3] 02411          blo    2f        ; No
                          02412
 B0EE 09 06           [2] 02413          ora    #$06
                          02414
 B0F0                     02415 2:
 B0F0 C0 A0           [2] 02416          cpy    #$A0      ; Does the upper nybble need adjustment?
 B0F2 90 02 (B0F6)  [2/3] 02417          blo    2f        ; No
                          02418
 B0F4 09 60           [2] 02419          ora    #$60
                          02420
 B0F6                     02421 2:
 B0F6 18              [2] 02422          clc              ; Do the adjustment(s)
 B0F7 65 15           [3] 02423          adc    Reg_A
 B0F9 85 15           [3] 02424          sta    Reg_A
 B0FB AA              [2] 02425          tax              ; Make a copy of the result to set flags
                          02426
 B0FC A5 14           [3] 02427          lda    Reg_CC
 B0FE 29 3B           [2] 02428          and    #!(f_S|f_Z|f_P)  ; Clear the flags we are updating
                          02429
 B100 C0 A0           [2] 02430          cpy    #$A0      ; The carry flag is set if upper nybble adjusted
 B102 90 02 (B106)  [2/3] 02431          blo    2f
                          02432
 B104 09 01           [2] 02433          ora    #f_C
                          02434
 B106                     02435 2:
 B106 1D AA9A       [4/5] 02436          ora    ValueFlags,X  ; Set the S, Z and P flags accordingly
 B109 85 14           [3] 02437          sta    Reg_CC
                          02438
 B10B 4C ADEE         [3] 02439          jmp    PCPlus1

I am sure it can be improved. The first thing to try is to build the adjustment byte from the carry and aux carry flags, then use the adc instruction in decimal mode to do the rest.

This is part of a program to test it:
Code:
        mvi     A,'9'           ; Test 9
        lxi     D,Desc9
        call    ShowTest

        mvi     A,99h
        mvi     C,9
        add     C
        daa

        mvi     H,008h          ; Expect 08 value
        mvi     C,1             ; Expect carry set
        call    TestIt

        jmp     2f

Desc9   db      '99 + 09 -> 08 + carry$'
;
2:
        jmp     0

;
;
;
TestIt:
        mvi     B,0             ; Start with no error

        mov     L,A             ; Save result

        mvi     A,0             ; Get carry flag
        ral

        cmp     C               ; Test carry flag
        jz      PassC

        call    ShowBadC

        inr     B               ; Indicate error

PassC:
        mov     A,L             ; Test result
        cmp     H
        jz      PassV

        call    ShowBadVal

        inr     B               ; Indicate error

PassV:
        mov     A,B
        ora     A
        jnz     Fail

        call    ShowPass

        ret

Fail:
        jmp     0

;
;
;
ShowTest:
        sta     TestID          ; Implant test ID

        push    PSW
        push    B
        push    H

        push    D

        mvi     C,9
        lxi     D,Test
        call    BDOS

        mvi     C,9
        lxi     D,TestID
        call    BDOS

        pop     D

        mvi     C,9
        call    BDOS

        mvi     C,2
        mvi     E,0Dh
        call    BDOS
        mvi     C,2
        mvi     E,0Ah
        call    BDOS

        pop     H
        pop     B
        pop     PSW

        ret

;
;
;
ShowPass:
        push    PSW
        push    B
        push    H

        mvi     C,9
        lxi     D,Passed
        call    BDOS

        pop     H
        pop     B
        pop     PSW

        ret

;
;
;
ShowBadVal:
        push    PSW
        push    B
        push    H

        mvi     C,9
        lxi     D,BadVal
        call    BDOS

        pop     H
        pop     B
        pop     PSW

        ret

;
;
;
ShowBadC:
        push    PSW
        push    B
        push    H

        mvi     C,9
        lxi     D,BadC
        call    BDOS

        pop     H
        pop     B
        pop     PSW

        ret

;
; Strings
;
Test    db      0Dh,0Ah,'Test $'
TestID  db      '0: $'
Passed  db      'Passed.',0Dh,0Ah,'$'
BadVal  db      'Value wrong.',0Dh,0Ah,'$'
BadC    db      'Carry flag wrong.',0Dh,0Ah,'$'


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 28, 2021 10:45 am 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
I think this should work. It might be a touch slower but will save quite a bit of memory and also keeps all your registers together.

Code:
f_C      equ $1
f_A      equ $10

Ptr      equ $12
Reg_CC   equ $14
Reg_A   equ $15
Reg_B   equ $17
Reg_H   equ $1B

; add more increment registers here

inc_A:   ldx   #Reg_A
      bne   incrCommon

inc_B:   ldx   #Reg_B

incrCommon:
      lda   $0,x
      inc   $0,x
      eor   $0,x
      tax
      jmp   Common

; add more decrement registers here

dcr_A:   ldx   #Reg_A
      bne   dcrCommon

dcr_B:   ldx   #Reg_B

dcrCommon:
      lda   $0,x
      dec   $0,x
      eor   $0,x
      tax

Common:   and   #$10      ; Isolate and save aux carry flag
      sta   Scratch

dcrMCommon:
      lda   Reg_CC   ; Clear the flags we are updating
      and   #$2A
      ora   Scratch   ; Fold in the aux carry flag
      ora   ValueFlags,Y  ; (AA90) Set the S, Z and P flags accordingly
      sta   Reg_CC
      jmp   PCPlus1   ; (ADE4)


Also this code snippet shows the very last line as a JMP to "dcrCommon" with an address of $B16A. But the above "dcrCommon" has an address of $ADF2. Are there 2 labels of dcrCommon?

Code:
                          02543 ;
                          02544 ; dcr B ; Decrement register B
                          02545 ;
 B17C                     02546 dcrB_:
 B17C A6 17           [3] 02547          ldx    Reg_B     ; Decrement the register
 B17E CA              [2] 02548          dex
                          02549
 B17F 8A              [2] 02550          txa              ; Make a copy of the result
 B180 45 17           [3] 02551          eor    Reg_B     ; Borrowed from upper nybble if its low bit changed
                          02552
 B182 86 17           [3] 02553          stx    Reg_B     ; Store the new value
                          02554
 B184 4C B16A         [3] 02555          jmp    dcrCommon


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 28, 2021 11:23 am 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
And for the Memory Register

Code:
; dcr M ; Decrement memory register
dcrM_:
      clc
      lda   $1A

Reg_L    ; Get target address
      adc   #<RAM80   ; (00 )
      sta   Ptr
      lda   Reg_H
      adc   #>RAM80   ; (B0)
      sta   Ptr+1

Replace this:
Code:
      ldy   #0
      lda   (Ptr),Y
      tax         ; Decrement the register
      dex

      txa         ; Make a copy of the result
      eor   (Ptr),Y   ; Borrowed from upper nybble if its low bit changed
      and   #$10      ; Isolate and save aux carry flag
      sta   Scratch

      txa           ; Store the new value
      sta   (Ptr),Y
      jmp   dcrMCommon   ; (AFD6)

with this:
Code:
      ldy #0
      lda (Ptr),y
      sta temp
      tax
      dex
      txa
      sta (Ptr),y
   
      ldx #temp
      jmp dcrCommon

Note: by doing it this way you and if you can remove the entry point and label "dcrMCommon", then you can also use the shorter method I posted before of:
Code:
Common:
      AND #$10
      ORA Reg_CC
      AND #$3A ; preserve the #$10 bit ( bit #4?)
      ORA ValueFlags,X
      STA Reg_CC


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 28, 2021 1:05 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
BB8 wrote:
As for "dcr M"
Code:
   clc
   lda RegL
   sta Ptr
   lda RegH
   adc #>Ram80
   sta Ptr+1
   ldy #0
   lda (Ptr),y

I noticed you are assuming that Ram80 is page-aligned.

There is no reason it could not be if I put the memory at the low end of the address space and my code at the high end.

Then I can lay out my variables like this:
Code:
Reg_H   rmb  1
PtrHL:
Reg_L   rmb  2

so that the code can just do this:
Code:
   clc
   lda RegH
   adc #>Ram80
   sta PtrHL+1
   ldy #0
   lda (PtrHL),y


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 28, 2021 1:10 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
IamRob wrote:
I think this should work. It might be a touch slower but will save quite a bit of memory and also keeps all your registers together.

That is a good idea the reuse code by loading the address of a variable in register X. My priority is currently speed, so I'll file this one away for later in case I need to cut the size of the code down.

IamRob wrote:
Also this code snippet shows the very last line as a JMP to "dcrCommon" with an address of $B16A. But the above "dcrCommon" has an address of $ADF2. Are there 2 labels of dcrCommon?

The project is a work in progress and I am posting snapshots from different versions of the assembly listing.


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 28, 2021 1:12 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
I fixed a couple of bugs:

the dcr/inr code was clearing the carry flag when it should not and the daa code was not clearing it before ORing in the new value.


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 28, 2021 2:21 pm 
Offline
User avatar

Joined: Sun Nov 01, 2020 10:36 am
Posts: 35
Location: Tatooine
here an 8080 Manual.

At page 15:
Attachment:
DAA.jpg
DAA.jpg [ 178.01 KiB | Viewed 844 times ]


Code:
Daa:
'low nibble
   fAdd := false
   if AuxCarry = 1
      fAdd := true
   elseif lownibble > 9
      fAdd := true
   endif
   if fAdd 
      value := value + 6
      set Carry flag if overflowed the 8th bit   ; <-- this is my guess
   endif
   set AuxCarry accordingly
'high nibble
   fAdd := false
   if Carry == 1
      fAdd := true
   elseif highnibble > 9
      fAdd := true
   endif
   if fAdd
      value := value + 60
      set Carry flag if overflowed
   end if
Set other flags accordingly to result


So: [not sure if correct]
- adjust +6 if lowernibble is > 9, or if AC was set
- adjust +60 if highernibble > 9, or if carry was set, or if carry gets set by lower nibble adjustment
- set AC to 1 if the adjustment set it, 0 otherwise
- set Carry to 1 if adjustment set it, leave unmodified otherwise
- set other flags based on the result


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 29, 2021 1:29 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
BB8 wrote:


Thanks for reminding me of that.

Generally, I prefer the Intel 8080 Microcomputer Users Manual of 1975 as my reference on the 8080.
Next is either the Intel 8080-8085 Assembly Language Programming manual of 1977 or the Osborne book 8080a/8085 Assembly Language Programming.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 29, 2021 11:30 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
It has been a fruitful day of hacking.

The emulator has implemented enough instructions so that MBASIC can run this program:
Code:
100 print "Hello "+"world."
110 gosub 200
120 end
200 for i=10 to 1 step -1 : print i : next i : print "Liftoff...we have liftoff!"
210 return


A bit of history...

Three weeks ago, I set out to start implementing an emulator to run FLEX programs written for the 6800 on the 6502. Immediately, there were significant issues which required organized thought instead of merely hacking through them.

So the next day, I pivoted to an emulator to allow running 8080 programs written for the CP/M operating system.

* the 8080 is an easier processor to simulate. Many instructions do not change the status flags.
* the 8080 has input and output instructions. It is not necessary to emulate memory-mapped I/O so an emulator is simpler and likely faster.
* The CP/M operating system has a simpler API than FLEX.

The initial version runs in the 6800 FLEX environment. After a week of good process, work began on a version for the 6502.

The 6502 version has caught up with its 6800 sibling and now both progress together in lockstep.

Current efforts run completely in a bare emulator environment so that I do not have to boot FLEX to test and I do not have to continually move files onto a virtual disk. For faster turnaround, the 8080 program is built into the emulator image. There is no disk access provided to the simulated program at this time.

The test programs used so far include:

* The Space Voyage game paraphrased from the original 6800 code to the 8080 instruction set.
* DDT, the CP/M debugger.
* MBASIC, the CP/M version of Microsoft BASIC


Top
 Profile  
Reply with quote  
PostPosted: Tue Mar 30, 2021 8:58 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
Great progress!


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 62 posts ]  Go to page Previous  1, 2, 3, 4, 5  Next

All times are UTC


Who is online

Users browsing this forum: Ask Jeeves [Bot] and 19 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: