Jump to content





Part 8 of 11 -- Simple Assembly for Atari BASIC - Convert Integer to Bit String

Posted by kenjennings, 01 August 2016 · 400 views

6502 Assembly mac/65 Atari BASIC Atari 8-bit

Convert Integer To Bit String
 
==============================================================  
 
Part 1 - Introduction
http://atariage.com/...or-atari-basic/
 
Part 2 - Learn 82.7% of Assembly Language in About Three Pages
http://atariage.com/...or-atari-basic/
 
Part 3 - The World Inside a USR() Routine 
http://atariage.com/...or-atari-basic/
 
Part 4 - Implement DPEEK() 
http://atariage.com/...or-atari-basic/
 
Part 5 - Implement DPOKE  
http://atariage.com/...or-atari-basic/
 
Part 6 - Various Bit Manipulations
http://atariage.com/...or-atari-basic/
 
Part 7 - Convert Integer to Hex String 
http://atariage.com/...or-atari-basic/
 
Part 8 - Convert Integer to Bit String
http://atariage.com/...or-atari-basic/
 
Part 9 - Memory Copy 
http://atariage.com/...or-atari-basic/
 
Part 10 - Binary File I/O  Part 1 (XIO is Broken) 
http://atariage.com/...or-atari-basic/
 
Part 11 - Binary File I/O  Part 2 (XIO is Broken)
http://atariage.com/...-basic-the-end/
 
==============================================================
 
 
This machine language routine converts a 16-bit integer into a string of values representing 0 and 1 bits.   This is useful for visualizing bits in a byte, word, or address for diagnostic purposes.  This is practical on the Atari for displaying character set or player/missile graphics data in a human-readable way.  This routine has a different number of arguments from the INT2HEX string conversion routine and so is presented on its own. 
 
This routine has similar problems to solve as the INT2HEX routine.  It must deal with the low byte stored in memory before the high byte, because it must output the bit values in order of highest bits first and lowest bits last.  (e.g 258 or hex $0102 should be output as “0000000100000010” though it is stored as $02 $01).   
 
Converting a byte to a string of bits is fairly easy in 6502.  In general terms: test the high bit and output a “0” or a “1” as needed, then shift the bits of the byte and repeat until 8 bits have been extracted.  The routine below includes a touch of customization – the USR() routine may also specify the characters (ASCII/ATASCII) used to represent “0” and “1”. 
 
INT2BITS in Mac/65 Assembler Code
 

0100 ; INT2BITS
0105 ;
0110 ; Convert 16-bit integer into
0115 ; string of bits.
0120 ;
0125 ; INT2BITS USR 2 arguments:
0130 ; Value  == Integer to convert.
0135 ; StrAdr == Address of string
0140 ;           which must be able
0145 ;           to hold 16 characters
0150 ; Zero   == Character used for
0155 ;           clear bit.
0160 ; One    == Character used for
0165 ;           set bit.
0170 ;
0175 ; USR return value 0 means no
0180 ; conversion.  Non-Zero means
0185 ; STRADR contains data.
0190 ;
0195 ; Use the FR0/FR1 FP register.
0200 ; The return value for BASIC
0205 ; goes in FR0.
0210 ; No FP is used so all of FR0
0215 ; (and more FP registers) can
0220 ; be considered available.
0225 ;
0230 ZRET =  $D4     ; FR0 $D4/$D5 Return value
0235 ZARGS = $D5     ; $D6-1 for arg Pulldown loop
0240 ZONE =  $D6     ; FR0 $D6/$D7 char for "1"
0245 ZZERO = $D8     ; FR0 $D8/$D9 char for "0"
0250 ZSTR =  $DA     ; FR0 $DA/$DB StrAdr
0255 ZVAL =  $DC     ; FR0 $DC/$DD Integer value
0260 ;
0265     .OPT OBJ
0270 ;
0275 ; Arbitrary. This is relocatable.
0280 ;
0285     *=  $9300
0290 ;
0295 INIT
0300     LDA #$00    ; Make sure return
0305     STA ZRET    ; value is cleared
0310     STA ZRET+1  ; by default.
0315     PLA         ; Get argument count
0320     BEQ EXIT    ; Shortcut for no args
0325     ASL A       ; Now number of bytes
0330     TAY
0335     CMP #$08    ; Integer Value1, StrAdr, Zero, One
0340     BEQ PULLDOWN
0345 ;
0350 ; Bad args. Clean up for exit.
0355 ;
0360 DISPOSE ;       any number of args
0365     PLA
0370     DEY
0375     BNE DISPOSE
0380     RTS         ; Abandon ship
0385 ;
0390 ; This code works the same
0395 ; for 1, 4, 8 ... arguments.
0400 ;
0405 PULLDOWN
0410     PLA
0415     STA ZARGS,Y
0420     DEY
0425     BNE PULLDOWN
0430 ;
0435 ; Arg validation.
0440 ; StrAdr may not be null.
0445 ; Zero and One may not
0450 ; be equal.
0455 ;
0460     LDA ZSTR
0465     ORA ZSTR+1
0470     BEQ EXIT    ; zstr is null
0475     LDA ZZERO
0480     CMP ZONE
0485     BEQ EXIT    ; zero == one
0490 ;
0495 ; Break Integer Value into
0500 ; bytes and test bits from
0505 ; high to low...
0510 ;
0515     LDX #$01    ; X index bytes.
0520     ;           Y is already 0
0525     ;           Y index string.
0530 ;
0535 WALK_BITS
0540     CLC
0545     ASL ZVAL,X  ; Shift out bit
0550     LDA ZZERO   ; Assume Zero
0555     BCC COPY2STR ; Copy if so
0560     LDA ZONE    ; Actually One
0565 COPY2STR
0570     STA (ZSTR),Y
0575     INY         ; Next string position
0580     CPY #$08    ; End of high byte?
0585     BEQ NEXT_BYTE ; Yes. Dec index.
0590     CPY #$10    ; End of low byte?
0595     BNE WALK_BITS ; No, loop again.
0600 NEXT_BYTE
0605     DEX         ; 1, 0 will continue
0610     BPL WALK_BITS ; -1 ends loop
0615 END_LOOP
0620 ;
0625     INC ZRET    ; Successful return
0630 EXIT
0635     RTS
0640 ;
0645     .END
 
The variable validation has a trivial addition to make sure the character values for “0” and “1” are not the same: 
0475     LDA ZZERO
0480     CMP ZONE
0485     BEQ EXIT    ; zero == one
 
Unlike the hex conversion there is just one part to the bit conversion: 
0515     LDX #$01    ; X index bytes.
0520     ;           Y is already 0
0525     ;           Y index string.
0530 ;
0535 WALK_BITS
0540     CLC
0545     ASL ZVAL,X  ; Shift out bit
0550     LDA ZZERO   ; Assume Zero
0555     BCC COPY2STR ; Copy if so
0560     LDA ZONE    ; Actually One
0565 COPY2STR
0570     STA (ZSTR),Y
0575     INY         ; Next string position
0580     CPY #$08    ; End of high byte?
0585     BEQ NEXT_BYTE ; Yes. Dec index.
0590     CPY #$10    ; End of low byte?
0595     BNE WALK_BITS ; No, loop again.
0600 NEXT_BYTE
0605     DEX         ; 1, 0 will continue
0610     BPL WALK_BITS ; -1 ends loop
0615 END_LOOP
 
There are two loops happening here.  One loop, indexed by X, counts in reverse, 1 to 0, from the high byte to the low byte of the integer.  Within that loop is the activity to output all the bits in the byte which is done as part of the loop indexed by Y to count from character position 0 to 15.  When the Y  loop reaches values 8 and 16 it signals that all the bits in the byte have been output, so then it is time to continue the outer loop to get the next byte.  The Y index is thus incremented 8 times for each decrement (one byte) of the X loop. 
 
As with the integer to hexadecimal conversion this utility receives the address of the output string, not its control information, so it can only fill in the bytes in the string and can't change the size as BASIC understands it.  A BASIC program using the results of the conversion must insure that it populates the length of the string prior to calling the conversion utility: 
10 DIM A$(16)
20 A$="                ":REM 16 SPACES
 
Now that we have routines to convert values into hex strings and bit representation, the test program for BITS can be revisited to provide improved diagnostic information. 
 
 
Testing BITS Again (with INT2HEX and INT2BITS)
 
Below is the Atari BASIC TESTBITS.BAS program upgraded with the new conversion utilities to produce much more sensible output illustrating the changes in the bits:  
100 REM TESTBIT2.BAS
105 REM TEST BIT OPERATIONS UTILITY 2
110 GRAPHICS 0:POKE 710,0:POKE 82,0
115 DIM B(3,1),N$(21),HX$(5),BIT$(17)
120 N$="OR ANDEORLSRLSLRORROL"
125 HX$="    ":BIT$="                "
130 AZERO=48:AONE=49:REM ATASCII 0 1
135 REM ENUMERATE BIT OPERATIONS
140 RESTORE 150
145 READ BOR,BAND,BEOR,BLSR,BLSL,BROR,BROL
150 DATA 1,2,3,4,5,6,7
155 GOSUB 10000:REM BITS UTILITY
160 RESTORE 190:REM BIT PATTERNS
165 FOR X=0 TO 1
170 FOR Y=0 TO 3
175 READ D:B(Y,X)=D
180 NEXT Y
185 NEXT X
190 DATA 0,257,0,257,0,0,258,258
195 REM
200 REM TEST OR, AND, EOR
205 REM
210 FOR OPER=BOR TO BEOR
215 FOR Y=0 TO 3
220 ? "   ";
225 VALUE=B(Y,0):GOSUB 405
230 ? N$(OPER*3-2,OPER*3);
235 VALUE=B(Y,1):GOSUB 405
240 ? "=== =====   ==================="
245 VALUE=USR(BITS,OPER,B(Y,0),B(Y,1))
250 ? "   ";:GOSUB 405
255 ?
260 NEXT Y
265 NEXT OPER
270 REM
275 REM TEST SHIFT AND ROTATE
280 REM
285 V=129:REM $81
290 FOR OPER=BLSR TO BROL
295 ? "Testing ";N$(OPER*3-2,OPER*3);" on Value ";
300 VALUE=V:GOSUB 505
305 FOR Y=0 TO 8
310 ? N$(OPER*3-2,OPER*3);" ";Y;" = ";
315 VALUE=USR(BITS,OPER,V,Y)
320 GOSUB 505
325 NEXT Y
330 ?
335 NEXT OPER
340 END
400 REM
401 REM OUTPUT INTEGER CONVERTED TO
402 REM USEFUL FORMAT:
403 REM $HEX = BITS BITS BITS BITS
404 REM
405 RH=USR(INT2HEX,VALUE,ADR(HX$))
406 RB=USR(BIT2STR,VALUE,ADR(BIT$),AZERO,AONE)
407 ? " $";HX$;" = ";BIT$(1,4);" ";BIT$(5,8);" ";BIT$(9,12);" ";BIT$(13,16)
408 RETURN
500 REM
501 REM OUTPUT BYTE VALUE CONVERTED TO
502 REM USEFUL FORMAT:
503 REM $HEX = BITS BITS
504 REM
505 RH=USR(INT2HEX,VALUE,ADR(HX$))
506 RB=USR(BIT2STR,VALUE,ADR(BIT$),AZERO,AONE)
507 ? "$";HX$(3,4);" = ";BIT$(9,12);" ";BIT$(13,16)
508 RETURN
9997 REM
9998 REM SETUP ML UTILITIES
9999 REM
10000 DIM BT$(162),B2S$(67),I2H$(72)
10001 BITS=ADR(BT$)
10002 RESTORE 27000:? "Loading BITS"
10003 FOR I=0 TO 161
10004 READ D:POKE BITS+I,D
10005 NEXT I:?
10006 BIT2STR=ADR(B2S$)
10007 RESTORE 25000:? "Loading BIT2STR"
10008 FOR I=0 TO 66
10009 READ D:POKE BIT2STR+I,D
10010 NEXT I:?
10011 INT2HEX=ADR(I2H$)
10012 RESTORE 26000:? "Loading INT2HEX"
10013 FOR I=0 TO 71
10014 READ D:POKE INT2HEX+I,D
10015 NEXT I:?
10016 RETURN
24996 REM H1:INT2BITS.OBJ
24997 REM SIZE  = 67
24998 REM START = 37632
24999 REM END   = 37698
25000 DATA 169,0,133,212,133,213,104,240
25001 DATA 57,10,168,201,8,240,5,104
25002 DATA 136,208,252,96,104,153,213,0
25003 DATA 136,208,249,165,218,5,219,240
25004 DATA 33,165,216,197,214,240,27,162
25005 DATA 1,24,22,220,165,216,144,2
25006 DATA 165,214,145,218,200,192,8,240
25007 DATA 4,192,16,208,236,202,16,233
25008 DATA 230,212,96
25996 REM H1:INT2HEX.OBJ
25997 REM SIZE  = 72
25998 REM START = 37888
25999 REM END   = 37959
26000 DATA 169,0,133,212,133,213,104,240
26001 DATA 62,10,168,201,4,240,5,104
26002 DATA 136,208,252,96,104,153,213,0
26003 DATA 136,208,249,5,215,240,40,216
26004 DATA 162,1,181,216,74,74,74,74
26005 DATA 145,214,181,216,41,15,200,145
26006 DATA 214,200,202,16,237,136,177,214
26007 DATA 201,10,144,2,105,6,105,48
26008 DATA 145,214,136,16,241,230,212,96
26996 REM H1:BITS.OBJ
26997 REM SIZE  = 162
26998 REM START = 38144
26999 REM END   = 38305
27000 DATA 169,0,133,212,133,213,104,240
27001 DATA 43,10,168,201,6,240,5,104
27002 DATA 136,208,252,96,104,153,213,0
27003 DATA 136,208,249,164,218,240,21,136
27004 DATA 240,19,136,240,29,136,240,39
27005 DATA 136,240,49,136,240,61,136,240
27006 DATA 73,136,240,90,96,165,216,5
27007 DATA 214,133,212,165,217,5,215,133
27008 DATA 213,96,165,216,37,214,133,212
27009 DATA 165,217,37,215,133,213,96,165
27010 DATA 216,69,214,133,212,165,217,69
27011 DATA 215,133,213,96,165,216,133,212
27012 DATA 166,214,240,208,74,202,208,252
27013 DATA 133,212,96,165,216,133,212,166
27014 DATA 214,240,193,10,202,208,252,133
27015 DATA 212,96,165,216,133,212,166,214
27016 DATA 240,178,24,106,144,2,9,128
27017 DATA 202,208,247,133,212,96,165,216
27018 DATA 133,212,166,214,240,158,24,42
27019 DATA 144,2,9,1,202,208,247,133
27020 DATA 212,96
 
Here is the kinder, gentler TESTBIT2 output with the values and bits illustrated:


Loading BITS
 
Loading BIT2STR
 
Loading INT2HEX
 
    $0000 = 0000 0000 0000 0000
OR  $0000 = 0000 0000 0000 0000
=== =====   ===================
    $0000 = 0000 0000 0000 0000
 
    $0101 = 0000 0001 0000 0001
OR  $0000 = 0000 0000 0000 0000
=== =====   ===================
    $0101 = 0000 0001 0000 0001
 
    $0000 = 0000 0000 0000 0000
OR  $0102 = 0000 0001 0000 0010
=== =====   ===================
    $0102 = 0000 0001 0000 0010
 
    $0101 = 0000 0001 0000 0001
OR  $0102 = 0000 0001 0000 0010
=== =====   ===================
    $0103 = 0000 0001 0000 0011
 
    $0000 = 0000 0000 0000 0000
AND $0000 = 0000 0000 0000 0000
=== =====   ===================
    $0000 = 0000 0000 0000 0000
 
    $0101 = 0000 0001 0000 0001
AND $0000 = 0000 0000 0000 0000
=== =====   ===================
    $0000 = 0000 0000 0000 0000
 
    $0000 = 0000 0000 0000 0000
AND $0102 = 0000 0001 0000 0010
=== =====   ===================
    $0000 = 0000 0000 0000 0000
 
    $0101 = 0000 0001 0000 0001
AND $0102 = 0000 0001 0000 0010
=== =====   ===================
    $0100 = 0000 0001 0000 0000
 
    $0000 = 0000 0000 0000 0000
EOR $0000 = 0000 0000 0000 0000
=== =====   ===================
    $0000 = 0000 0000 0000 0000
 
    $0101 = 0000 0001 0000 0001
EOR $0000 = 0000 0000 0000 0000
=== =====   ===================
    $0101 = 0000 0001 0000 0001
 
    $0000 = 0000 0000 0000 0000
EOR $0102 = 0000 0001 0000 0010
=== =====   ===================
    $0102 = 0000 0001 0000 0010
 
    $0101 = 0000 0001 0000 0001
EOR $0102 = 0000 0001 0000 0010
=== =====   ===================
    $0003 = 0000 0000 0000 0011
 
Testing LSR on Value $81 = 1000 0001
LSR 0 = $81 = 1000 0001
LSR 1 = $40 = 0100 0000
LSR 2 = $20 = 0010 0000
LSR 3 = $10 = 0001 0000
LSR 4 = $08 = 0000 1000
LSR 5 = $04 = 0000 0100
LSR 6 = $02 = 0000 0010
LSR 7 = $01 = 0000 0001
LSR 8 = $00 = 0000 0000
 
Testing LSL on Value $81 = 1000 0001
LSL 0 = $81 = 1000 0001
LSL 1 = $02 = 0000 0010
LSL 2 = $04 = 0000 0100
LSL 3 = $08 = 0000 1000
LSL 4 = $10 = 0001 0000
LSL 5 = $20 = 0010 0000
LSL 6 = $40 = 0100 0000
LSL 7 = $80 = 1000 0000
LSL 8 = $00 = 0000 0000
 
Testing ROR on Value $81 = 1000 0001
ROR 0 = $81 = 1000 0001
ROR 1 = $C0 = 1100 0000
ROR 2 = $60 = 0110 0000
ROR 3 = $30 = 0011 0000
ROR 4 = $18 = 0001 1000
ROR 5 = $0C = 0000 1100
ROR 6 = $06 = 0000 0110
ROR 7 = $03 = 0000 0011
ROR 8 = $81 = 1000 0001
 
Testing ROL on Value $81 = 1000 0001
ROL 0 = $81 = 1000 0001
ROL 1 = $03 = 0000 0011
ROL 2 = $06 = 0000 0110
ROL 3 = $0C = 0000 1100
ROL 4 = $18 = 0001 1000
ROL 5 = $30 = 0011 0000
ROL 6 = $60 = 0110 0000
ROL 7 = $C0 = 1100 0000
ROL 8 = $81 = 1000 0001

 

 The output is considerably easier to understand.  So neat, so organized, so obvious and informative! 
 
 
Below are the source files and examples of how to load the machine language routine into BASIC included in the disk image and archive: 
 
  
INT2BITS File List:
 
INT2BITS.M65    Saved Mac/65 source
INT2BITS.L65    Mac/65 source listing
INT2BITS.T65    Mac/65 source listed to H6: (linux)
INT2BITS.ASM    Mac/65 assembly listing
INT2BITS.TSM    Mac/65 assembly listing to H6: (linux)
INT2BITS.OBJ    Mac/65 assembled machine language program (with load segments)
INT2BITS.BIN    Assembled machine language program without load segments
INT2BITS.LIS    LISTed DATA statements for INT2BITS.BIN routine.
INT2BITS.TLS    LISTed DATA statements for INT2BITS.BIN routine to H6: (linux)
 
MAKEI2B.BAS    BASIC program to create the INT2BITS.BIN file.  This also contains the INT2BITS routine in DATA statements.
MAKEI2B.LIS        LISTed version of MAKEI2B.BAS
MAKEI2B.TLS        LISTed version of MAKEI2B.BAS to H6: (linux)
 
TESTI2B.BAS        BASIC program that tests the INT2BITS USR() routines.
TESTI2B.LIS        LISTed version of TESTI2B.BAS.
TESTI2B.TLS        LISTed version of TESTI2B.BAS to H6: (linux)
 
 
 
 ZIP archive of files:
 
Attached File  Convert_Disk.zip (32.66KB)
downloads: 26
 
 
Tar archive of files (remove the .zip after download)
 
Attached File  Convert_Disk.tgz.zip (19.05KB)
downloads: 23
 
 
 
 
 
The Lord will fulfill his purpose for me; your steadfast love, O Lord, endures forever. Do not forsake the work of your hands.   
Psalm 138:8