Jump to content
IGNORED

AssemBASIC .. Learning assembly with the help of BASIC


SteveB
 Share

Recommended Posts

There are some of us who can think in assembly language. I am none of them. 

 

Especially when it comes to adapt a given algorithm, it feels difficult to code it in assembly. I tried to do it directly with Bresenham's line-drawing algorithm. I found a generic BASIC implementation without any comments or explaination and my first attempt of a straight port failed. It didn't work and I found some, but not all bugs. So I tried something new.

 

This was the code I found on the net:

10 REM === DRAW a LINE. Bresenham algorithm from (x1,y1) to (x2,y2)
20 DX = ABS(X2 - X1) :SX = -1 :IF X1 - x2 < 0 THEN SX = 1
30 DY = ABS(Y2 - Y1) :SY = -1 :IF Y1 - y2 < 0 THEN SY = 1
40 ER = -DY : IF DX - dy > 0 THEN ER = DX
50 ER = INT(ER / 2)
60 PLOT X1,Y1
70 IF X1 = X2 AND Y1 = Y2 THEN RETURN
80 E2 = ER
90 IF E2 +dx > 0 THEN ER = ER - DY:X1 = X1 + SX
100 IF E2 -dy < 0 THEN ER = ER + DX:Y1 = Y1 + SY
110 GOTO 60 

 

It was easily adapted for XB, I used CALL HCHAR(Y1,X1,30,1) for the PLOT() , played around in the standard graphics mode and was quite pleased with the result.

 

As mentioned, my direct attempt to code this in TMS9900 failed. So I started to change my BASIC program more and more to use only very simple statements, only one each line, to come closer to AL. One important difference between high level languages and AL is the logic behind IF: In XB you have a "IF - THEN - DO" logic, in Assembly you have a "IF NOT THEN SKIP", as you can only bypass some code with Jxx instructions. Assembly has no line-numbers, so I used labels in BASIC as well (as in TIdBiT, xbas99 and TiCodEd) and came to the following, with assembler OpCodes in my mind:

 

SUB Bresenham(y1,x1,y2,x2,c)
  DX = ABS(X2 - X1)
  SX = 1
  IF X1 < X2 THEN LINE3A
  SX = SX-2

LINE3A:
  DY = ABS(Y2 - Y1)
  SY = 1
  IF Y1 < y2 THEN LINE3B
  SY = SY-2

LINE3B:
  ER = -DY
  IF DY > DX THEN LINE3C
  ER = DX

LINE3C:
  ER = INT(ER / 2)

LINE4:
  CALL HCHAR(Y1,X1,C,1)
  IF X1 <> X2 THEN LINE4A
  IF Y1 <> Y2 THEN LINE4A
  SUBEXIT

LINE4A:
  E3 = ER - DY
  E2 = ER + DX
  IF E2 < 0 THEN LINE4B
  ER = ER - DY
  X1 = X1 + SX

LINE4B:
  IF E3 >= 0 THEN LINE4
  ER = ER + DX
  Y1 = Y1 + SY
  GOTO LINE4
SUBEND

 

After testing this in all quadrants, I translated it line by line to assembly:

 

[... skiped the paramater readings into PY,PX,PY2,PX2,PCOLOR ... ]

LINE2
       LIMI 0
       MOV R11,@RETADR

LINE3                    
       MOV @PX2,R6         DX = ABS(X2 - X1) 
       S @PX,R6
       ABS R6
       MOV R6,@PDX         
       MOV @ONE,@PSX       SX = 1
       C @PX,@PX2          IF X1 < X2 THEN LINE3A
       JLT LINE3A          
       DECT @PSX           SX = SX-2   
     
LINE3A
       MOV @PY2,R6         DY = ABS(Y2 - Y1)   
       S @PY,R6
       ABS R6
       MOV R6,@PDY       
       MOV @ONE,@PSY       SY = 1
       C @PY,@PY2          IF Y1 < y2 THEN LINE3B
       JLT LINE3B
       DECT @PSY           SY = SY-2

LINE3B                     
       MOV @PDY,R6         ER = -DY
       NEG R6              
       MOV R6,@PER      
       C @PDY,@PDX         IF DY > DX THEN LINE3C
       JGT LINE3C
       MOV @PDX,@PER       ER = DX

LINE3C
       MOV @PER,R6         ER = INT(ER / 2) 
       SRA R6,1            
       MOV R6,@PER


LINE4            
       MOV @PY,R3          CALL PUTPIX(Y1,X1,C)
       MOV @PX,R4
       BL @PUTPX3          ** Draw Pixel at row R3 and Col R4

       C @PX,@PX2          IF X1 <> X2 THEN LINE4A
       JNE LINE4A          ** No? Continue drawing

       C @PY,@PY2          IF Y1 <> Y2 THEN LINE4A
       JNE LINE4A          ** No? Continue drawing
      
       MOV  @RETADR,R11    SUBEXIT
       LIMI 2
       RT           

LINE4A
       MOV @PER,@PE3	   E3 = ER - DY  // Part 1 - save PER to PE3
       MOV @PER,R6         E2 = ER + DX  // R6 as PE2
       A @PDX,R6           
       JLT LINE4B          IF E2 < 0 THEN LINE4B
       S @PDY,@PER         ER = ER - DY 
       A @PSX,@PX          X1 = X1 + SX

LINE4B 
       MOV @PE3,R6         E3 = ER - DY  // Part 2 - Subtract DY
       S @PDY,R6           
       JGT LINE4           IF E3 >= 0 THEN LINE4
       JEQ LINE4
       A @PDX,@PER         ER = ER + DX 
       A @PSY,@PY          Y1 = Y1 + SY
       JMP LINE4           GOTO LINE4

 

It might not be the most elegant and efficient code, but it worked immediately. From here I might improve and optimize the code, but there is no substitute for running code. 

 

I wanted to share this approach with anyone who struggles with a direct port of a high-level algorithm to assembly.

 

Steve

 

 

  • Like 13
  • Thanks 1
Link to comment
Share on other sites

You remind of an old byte magazine article about ADA. The author had the job of hand translating people's ADA code to Assembler in the time before a compiler was ready.

You are that guy now! :) 

  • Like 1
Link to comment
Share on other sites

Back in the days I frequently implemented algorithms in Pascal, tested them and then converted to assembly, if they worked as they should, just not fast enough. Since you can use integer variables in Pascal, it's possible to test the algorithm even more closely, as any issue that's masked by BASIC's floating point numbers only does show up.

Assembly from the beginning I did when it wasn't possible to use this approach. Sometimes I wrote algorithms in a pseudo language, which was based on Pascal. I still do that occasionally today.

 

The Quicksort program I've shown here earlier was first a Pascal program. My bitmap line drawing routine too. It doesn't implement Bresenham's algorithm, since I wasn't aware of it at the time. Still reasonably fast.

Edited by apersson850
  • Like 1
Link to comment
Share on other sites

It is great to see examples of this in practice.  I believe that whether someone "thinks" in Assembly or not, programming pieces (or the whole) in a higher-level language can be extremely helpful to prove a concept and/or to give you a roadmap to follow, especially if the higher-level code works!   I tested my 'wordle clone' routines in XB before writing them in assembly - it was easier to debug the concept over many iterations and gave me some relatively instant feedback.   The opposite direction works as well: re-writing an existing assembly routine in XB can help you understand how the assembly code works. 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

You might want to look at an old series of articles from Don Gronos (published in the Enthusiast99 magazine, IIRC): Assembly Translated BASIC. The series was never completed, but each installment approached a few more BASIC commands in much the same way you are doing. If nothing else, the articles may help you extend what you're doing here a little bit faster.

 

I really liked the GATB concept, so your post here immediately piqued my interest. :)

  • Like 1
Link to comment
Share on other sites

I find BASIC very helpful in putting together my little assembly machines - in large part because it is devoid of data structures more abstract than variables and arrays. Upon translating BASIC to assembly, my comments are often the original BASIC statements to describe a routine that follows, supplemented by comments on each assembly statement.  Example:

 

image.thumb.png.cc1364a587da7b4779719a7afa4ba1d1.png

etc. etc.....

 

(I also use a lot of vertical white space to delineate small chunks of code).

Edited by Reciprocating Bill
  • Like 4
Link to comment
Share on other sites

I just wanted to know if there were some hidden good reason for the construct. Besides, unless you need the memory locations TS and TW for other things too, or are out of free registers, you same cycles by comparing a register with a register, instead of with a memory location.

Link to comment
Share on other sites

On 1/17/2023 at 1:27 PM, apersson850 said:

I just wanted to know if there were some hidden good reason for the construct. Besides, unless you need the memory locations TS and TW for other things too, or are out of free registers, you same cycles by comparing a register with a register, instead of with a memory location.

Those (and some other memory locations) were used as global constants controlling several parameters referenced throughout several phases of the program (maze creation, display, and solution). Keeping that many registers otherwise untouched throughout would have made things tight, register-wise. Also, apropos the thread topic, the memory labels mirror several variable names used in the BASIC program I was converting to assembly.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...