Flipping a byte

General discussions related to the Altair 8800 Clone

Flipping a byte

Postby mail@gabrielegan.com » December 27th, 2015, 6:26 am

I'd like to post some Assembler code here. I've tried putting it inside [code][/code] tags but in the preview it's all messed up, as if the TABs in my code aren't expanding to the same widths as they do in Windows Notepad (where I'm editing the code), so columns don't line up. Also, the long lines wrap, which looks bad. I don't want to post untidy looking code, so can anyone advise on making it look right? I can either post the .ASM file or the .PRN file that the Loader generates: which is best?

Gabriel Egan
Minimal Computing Lab
De Montfort University
mail@gabrielegan.com
 
Posts: 104
Joined: October 11th, 2014, 8:12 am

Re: Flipping a byte

Postby Tor » December 27th, 2015, 11:09 am

Code: Select all
zero
    four
  two

The solution is to use space indentation, not tabs. Tabs can expand to just about anything, and aren't very useful outside of old typewriters. (They may indeed look useful, but it's just not portable, even locally. They create more problems than they solve.)
I don't know about Notepad, but many editors have a function to 'untabify' a file, replacing tabs with spaces so that it looks the same as in that editor, just with spaces.

The long lines I don't have a solution for. When I write code I limit line lengths to 72 or 80 chars, depending on what it's for.
Tor
 
Posts: 12
Joined: June 20th, 2013, 5:34 pm
Location: Norway/Japan

Re: Flipping a byte

Postby mail@gabrielegan.com » December 27th, 2015, 12:05 pm

Thanks Tor!

Righto, here goes ...

It came up in the discussion of paper-tape art a while ago that it would be handy to be able to 'flip' an image by transposing all the bits in a byte so that the 7th and 0th bit were swapped with one another, and the 6th and 1st, and the 5th and 2nd, and the 4th and 3rd. That would allow you to have the image appear on the side of the tape that doesn't have a 'this way' arrow printed on it. (As someone pointed out, you could just turn the tape over and rotate it, but that'd reverse your image left-to-right, which you might not want that -- for example if it contains writing.)

To get myself into 8080 assembler programming under CP/M I wrote the following small program to do this byte-flipping. It works by repeatedly rotating-right the byte to be mirrored and if a 1 pops out then adding to a running total (stored in register E) a byte (stored in register D, for 'declining') that starts out at 10000000 and then is rotated to become 01000000 then 00100000 and so on until it's 00000001. I'd be interested in criticisms of the code's inefficiency. I'm sure I'm swapping bytes between registers more than I need to do. I'm not so much interested in a better algorithm (I'm sure they exist) as in polishing the implementation of this one and thereby learning to code more elegantly.

Gabriel Egan
Minimal Computer Lab
De Montfort University

Code: Select all
;    ***************************************************************************
;    * BIT-FLIPPER Flip bits (7<>0, 6<>1, 5<>2, 4<>3) in each byte in a string *
;    ***************************************************************************
;    This program starts at 0100h
;    The string starts at 8000h
;    The stack is wherever CP/M puts it
;    H,L holds the address where the string starts
;    B,C holds the length of the string
;    D holds a declining byte (from 10000000 to 01000000 to 00000001)
;      that gets added to a number (stored in E) that that builds up the mirrored byte
;    E stores the mirrored byte until we're ready to put it back into memory

        org    0100h
        LXI    H, 8000h    ; Put address of string-start into H,L pair
        LXI    B, 0005h    ; Put string length (16 bits) into B,C pair
        CALL NEXTBYTE      ; Call the subroutine for mirroring
        RET                ; go back to CP/M
   
NEXTBYTE MVI E,0       ; Initialize E
        MVI D,10000000b; Initialize pointer to the bit we're dealing with
NEXTBIT MOV A,M        ; Load accum. with byte we're working on (pointed to by H,L)
        RRC            ; Pop off the rightmost bit to test
        MOV M,A        ; Put the rotated byte back in memory because we'll want to
                       ;   do that whether or not we popped a zero
        JNC POPPEDZERO ; If the rightmost bit was zero, continue with next bit
        MOV A,E        ; Get the partially completed mirror-byte into Acc. so we can add to to it
        ADD D          ; Add the declining byte to it
        MOV E,A        ; Put the result back in E
POPPEDZERO    MOV A,D  ; Get the declining byte so we can rotate it
        RRC            ; Rotate the decline byte
        MOV D,A        ; Put the declining byte back into D
        JNC NEXTBIT    ; If a 0 popped out on the right side then D wasn't 00000001
                       ;  so there are more bits to do
        MOV M,E        ; if 1 popped we're done with this byte so write it to memory
                       ; And let's work on the next byte
        INX H          ; Point to the next location in memory to work on
        DCX B          ; Lower pointer to number of bytes
        MVI A,0        ; Compare C to 0 and . . .
        CMP C          ; . . . go back around loop if . . .
        JNZ NEXTBYTE   ; . . . there are more bytes to do
        RET
   
mail@gabrielegan.com
 
Posts: 104
Joined: October 11th, 2014, 8:12 am

Re: Flipping a byte

Postby AltairClone » December 27th, 2015, 4:33 pm

Gabriel,

You're off to a pretty good start! I assume you want to handle strings longer than 256 bytes since you're using a 16 bit byte counter in BC. In this case, the compare operation you do after the DCX B at the bottom of the loop won't work properly since it's only checking the LSB byte of the counter for zero.

Secondly, though this is changing your algorithm a bit, rather than shifting and adding D to create the mirror byte, you could simply shift the mirror byte the opposite direction as you shift the source byte. The RAL instruction, for example, will pull the value of the carry bit in as the new LS bit of the result.

Finally, I unrolled the first call to NXTBYTE, though this doesn't really affect much of anything.

I didn't update the code with the following comments, but it would be more efficient to save the source byte into a register after each shift rather than back into memory each time. The load and save to a register for each shift would save 4 cycles per bit or up to 32 cycles per byte. Unfortunately, you have no spare registers left unless you really only need to handle strings less than 256 bytes in length. Or, you could push/pop HL each time through the byte loop, but that would take 21 of the cycles you'd be saving, so it may no be worth it.

Mike

Code: Select all
;    ***************************************************************************
;    * BIT-FLIPPER Flip bits (7<>0, 6<>1, 5<>2, 4<>3) in each byte in a string *
;    ***************************************************************************
;    This program starts at 0100h
;    The string starts at 8000h
;    The stack is wherever CP/M puts it
;    H,L holds the address where the string starts
;    B,C holds the length of the string
;    D holds count of bits within a byte
;    E stores the mirrored byte until we're ready to put it back into memory
       
        org 0100h
        LXI H,8000h     ; put address of string-start into H,L pair
        LXI B,0005h     ; put string length (16 bits) into B,C pair
   
NXTBYTE MVI D,8         ; D=bit counter within a byte

NEXTBIT MOV A,M         ; A=byte we're working on (pointed to by H,L)
        RRC             ; shift source byte right into carry
        MOV M,A         ; save the rotated byte back in memory

        MOV A,E         ; A=partially completed mirror byte
        RAL             ; shift new bit left into the mirror byte     
        MOV E,A         ; save the temp result in E

        DCR D           ; decrement bit counter
        JNZ NEXTBIT     ; loop through all 8 bits

; The completely flipped byte is in A. Store back in memory at (HL),
;    then increment HL, decrement byte count in BC until BC=0.

        MOV M,A         ; store mirrored byte in memory
        INX H           ; point to the next location in memory to work on

        DCX B           ; decrement 16-bit byte counter
        MOV A,B         ; A=msbyte of counter
        ORA C           ; A=msbyte + lsbyte of counter
        JNZ NXTBYTE     ; loop while BC not zero

        RET             ; return to CP/M
   


Mike
AltairClone
Site Admin
 
Posts: 632
Joined: April 5th, 2013, 10:55 am

Re: Flipping a byte

Postby mail@gabrielegan.com » April 15th, 2016, 9:49 am

To my great frustration I only just now found time to get back to this.

I see what you did there, Mike, with the RRC followed by RAL so that the bit that was popped off the least-significant end of one register and into the carry bit by a right-shift gets pushed into the least-significant end of the other register by a left-shift. That's very cool!

I was aware that I hadn't made my code capable of flipping more than 256 bytes at a time as I was only using half the B,C register. I was thinking that I'd get this working and then figure out how to check when the full 16 bits of B,C reaches zero. I had a whole complex plan for doing this check and then you go and point out that all it takes is a logical OR of B with C: if the result of that OR is zero, they both are zero. Brilliant! -- I'll be reusing that trick I'm sure.

Thanks very much for this. My next project is a 'paragraph reflower' routine that takes any multi-line string of ASCII text and reflows it to a new, arbitrary margin width (set by the user) by moving the LF/CR codes around.

Regards

Gabriel
mail@gabrielegan.com
 
Posts: 104
Joined: October 11th, 2014, 8:12 am


Return to General Discussions

Who is online

Users browsing this forum: No registered users and 14 guests

cron