flat assembler
Message board for the users of flat assembler.

Index > MenuetOS > [Newbie question] Steps to learn ASM?

Author
Thread Post new topic Reply to topic
S0urcerr0r



Joined: 19 Oct 2010
Posts: 5
Location: SWEDEN
S0urcerr0r 19 Oct 2010, 02:48
Hi everyone!

As i understand Assembler is the way to go with MenuetOS... i know a little C/C++ but i want to learn assembler instead. Even a beginner must be able to learn Asm, cause many years ago it didnt even exist HL languages, so Asm/Machine Code was the only option...

However - Ive been trying to learn Asm for a while now, and i dont know where to start...

Usually when learning something new i dont make any certain priority order - i just read and read everything i come by, but Assembler turned out to be like a wall.

This is why i need help to know how to proceed - i have way too bad memory to learn everything at once, like i usually try to do... Ive prepared some questions:

1.) Where should i start?
Im unsure what area i should start learning... Registers, Flags, Instructions, Ints? ...any priority order? ...is there anything i must understand or know before i can even understand the concepts of Regs, Flags, Instructions and Ints?

2.) Which instructions are most important - in the beginning?
from what ive learned before, i can tell that "MOV" and "INT" are very important instructions... any other instructions that i cant do without in the beginning? ...i try to do as little workload as possible for the brain in the beginning cause asm is so overwhelming.

3.) [THE MOST IMPORTANT QUESTION] What do i need to "staticly" remember to code Asm?
do i need to staticly remember all "Instructions"? do i need to staticly remember all Int's? ...how about registers and flags? can i leave some Regs and Flags behind? ...Staticaly remembering ALL Instructions, Ints, Registers and Flags will probably be impossible... im willing to learn some of instructions statically but i need to know what i can leave in a reference book and what i need to statically carry in the brain.
im good with decimal numbers... do i need to statically know which binary- or hex number that represents a certain decimal number - or is it enough to only be self-sufficient with decimals, and use a little app for converting Decimal to Binary? do i need to know 32-bits or is it enough with 16 or 8?

4.) Are there any general rules that will ease the understanding of Assembler?
for example... different Instructions put the end-result in different registers - is there any rule i can follow to "understand" which register will recieve the result from a certain instruction?

5.) Ive taken up Registers, Flags, Instructions and Ints... is there anything else i must know about in the beginning? ...pointers? ...stacks? ...anything else?

6.) When reading other peoples Asm programs i notice they concist of very many different files... its not only just ONE BIG asm file... its plenty of smaller files... How does this system work? is there one "main" file that reference to the other files and execute them in a certain order? or do all of these files run simultaneously? ...do these files run in their entirity, or can a small part of a file be run before jumping to another file and continuing execution from there? ...because i lack this fundamental understanding i get really confused when seeing a program consisting of several smaller files - instead of just one single file.

7.) How important is it to have good memory?
as comparisson... i remember learning the names of 200 different trees and flowers... each of them had up to 4 "names" (family, species, ephitet, etc)... i found it quite difficult, BUT it was possible...
im afraid that assembler will mean learning massive amounts of information, but i hope im wrong. i hope that assembler programming will mostly mean learning the logical nature of a limited amount of instructions, registers and flags...

ive been studying some assembler tutorials and im still confused

with the guidance i can get from the answers to these questions i will retry reading all the Asm tutorials ive found. i think it will be much easier when i know which parts are most essential to progress in the other areas.

i couldnt imagine that Assembler would be so hard, but i have patience and im willing to spend many months to get an idea how to program assembler...

any information is much appreciated! Smile

PS. if no one is willing to answer these questions... can u maybe instead tell a little story how u learned Asm? ...like, which parts felt overwhelming at first until u realised it wasnt as difficult as u expected... currently, it feels like i need to know 100's of pages raw data statically in the brain.
Post 19 Oct 2010, 02:48
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20356
Location: In your JS exploiting you and your system
revolution 19 Oct 2010, 06:47
Rather than trying to run before you can walk ...

I would suggest you start by working with the examples that come with fasm, in your existing OS.

Running a whole new OS as a means to learn assembly seems quite ambitious.
Post 19 Oct 2010, 06:47
View user's profile Send private message Visit poster's website Reply with quote
S0urcerr0r



Joined: 19 Oct 2010
Posts: 5
Location: SWEDEN
S0urcerr0r 19 Oct 2010, 15:28
alright, thanks for replying, but i still dont know where to start...
ive been looking at sample programs... even with the simplest ones like "hello world" i dont really undestand 100% of what im doing...
as example:

im so confused that i dont really even understand what a Int is...
ive tried looking up many of these things on asm tutorials and wikipedia, but im only getting more confused with even more technical jargons.

when reading tutorials, many people claim that Int's are a feature of the BIOS.
...when i read such a thing, i start to think that everytime i call an Int in a asm program it executes some pre-compiled code in the BIOS - almost like if it was a high-level instruction.
however, with common sense i assume that Asm is purely low-level with 100% control of what opcodes i can feed the CPU with.


i suppose it wasnt a good idea that i posted in the MenuetOS thread - since its a bit off topic...

the reason i want to code for MenuetOS is that there are way too many layers of crap in windows7 / XP - which would add even more rules to respect while coding Asm... i want a much more pure environment - like MenuetOS.

MenuetOS depends on some simple standard drivers like VESA (graphics), and so on... i consider it a very basic platform - which i suppose is good when learning Asm...

and i dont want to code for ms dos since it isnt optimized for newer CPU's...
Post 19 Oct 2010, 15:28
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4046
Location: vpcmpistri
bitRAKE 19 Oct 2010, 20:00
1) Digital logic. CPU state (registers, flags, exceptions, etc.), instructions.

2) Whatever you find in reading sources. Most programmers only use a small subset: MOV, PUSH/POP, ADD/SUB, SHL/SHR, MUL/DIV. First volume of Intel manuals approach instructions in groups.

3) Use smart references. Trying to know everything all the time is kind of silly. What you use will be easy to recall, everything else will be a click away.

4) Most instructions are OP <dest>,<src>.

5) All algorithmic knowledge will be useful, and strong math skills help.

6) Organization is defined by the project - there is no standard.

7) see (3)


Most importantly, understand that learning happens in stages: there is a build-up of confusion and then a break-through to greater comprehension. Just be persistent and take as large of steps as you can - very small as first. Challenge yourself with puzzles, http://projecteuler.net/ for example.

If you are more of a hands on kind of learner then just start changing small programs: look-up instructions, make changes, execute, verify, repeat... Debuggers are a GREAT way to learn - seeing what each instruction does while stepping through code -- I still use this method every day.
Post 19 Oct 2010, 20:00
View user's profile Send private message Visit poster's website Reply with quote
ouadji



Joined: 24 Dec 2008
Posts: 1081
Location: Belgium
ouadji 19 Oct 2010, 21:27

200% agree with bitRAKE.
The digital and combinational logic !
it's the very basis !
That is impossible to circumvent to learn assembly!
The "old school" ... there is nothing better !
Quote:
Debugger is a GREAT way to learn <------- here is a magical advice from bitRAKE !
understand why your code does not work,
but more importantly ...
understand why it works.

_________________
I am not young enough to know everything (Oscar Wilde)- Image
Post 19 Oct 2010, 21:27
View user's profile Send private message Send e-mail Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 19 Oct 2010, 23:19
I second the debugger. Download olly, step some example program instruction by instruction, see effect of each instruction and how it's used. But do not dive into system API functions called by examples Smile
Post 19 Oct 2010, 23:19
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
S0urcerr0r



Joined: 19 Oct 2010
Posts: 5
Location: SWEDEN
S0urcerr0r 21 Oct 2010, 04:52
bitRAKE wrote:
[plenty of useful info]

BIG THANKS! Smile
now i know where to start.
i think ill buy some book about digital logic in english then - i suppose its best to learn all the "technical terms" in the correct language.
...ill also get that Intel manual u mentioned.

im not too good with complex mathematics (=never had the courses in college), but i'll read up on that as well.

if i'll ever gonna make a huge assembler program, i think i'll illustrate it on paper or in a graphical application... this will probably ease the requirements of needing to have a clear picture in the head of the complete program structure.

debugging (disassembling) is also something i'll look into when i get a little better - i already have a really hard time understanding properly commented code.
also... im so inefficent at using a debugger that i may accidently start debugging "data" instead of "code"... i guess even exe-files can contain data, and are not strictly code - if its text-data i may detect it fast, but if its sound or pictures, it may become a problem... when using debuggers ive noticed though, that such data is usually located at the end of the file.
i remember using hex-editors back in the old days (~1990) to change text in games, and sometimes even stats.

your final words were also very encouraging... assembler is something that have felt like a wall for a long time - everything about it requires so much mind processing and ability to remember... so i understand that it will probably take years until i can start writing video encoders (which is one of the future goals i have), and maybe even drivers for simple hardware.

thanks again!



ALSO... thanks to everyone else who posted here - it have all been helpful! Smile
Post 21 Oct 2010, 04:52
View user's profile Send private message Reply with quote
tallpeak



Joined: 19 Dec 2013
Posts: 6
tallpeak 21 Dec 2013, 07:00
I learned assembler out of a perceived need, in around 1985, when the only computer we had did a whopping 3mhz and ran a very slow, doubly-interpreted BASIC (which was written mostly in GPL bytecode to save space in the TI-99/4a). I had quit school at age 16 because it was so boring and because my father let me, signed up for an American Schools home schooling course, and the first book they sent me was a social-studies one, so of course I could only stand to read one page and I instead plowed right in and learned assembly. It helped that my father had concrete ideas of what he wanted to write (a Katakana teaching program, which we developed together...wish I still had a copy, with or without the source....), and needs to accomplish his ideas, such as the "plink" routine I wrote for his Sakura, Sakura song, and a screen editor which I wrote (complete with RLE-compression.) Anyway, I spent most of that year learning assembly-language, but really haven't done much of it these last 27-years hence, until recently when I got the bright idea to rewrite my project Euler #92 solution in asm, after writing it already in Haskell, F#, C, C#, and Scala. Oh, and SQL. Now I'm mostly learning functional programming languages, and I highly recommend it, however if you are determined, I recommend picking specific small challenges to start with, like a kaleidoscopic-drawing program (something I wrote in 1990 in C and have yet to get around to rewriting in Javascript or something modern), or to begin with simple things like calculations and printf to show your results. Possibly learn macros to make it less tedious, but I haven't.

I rather enjoyed rewriting Euler #92 in 64-bit ASM with FASMW.exe; it's a nice little IDE. I really should thank the author (thanks, if you are watching!) I'm more likely to switch from Windows to Linux than from Windows to MenueT, but perhaps his little OS will have use in small/embedded systems and maybe it would even be worth considering as an OS for a media console
(though I see I'd have to buy his media player application.

But I digress.

Maybe soon (tonight) I'll post some simple examples which I enjoyed writing recently, like my timing routine for a few versions of itoa/utoa, though I'm sure that topic has been beat to death.
Post 21 Dec 2013, 07:00
View user's profile Send private message Reply with quote
tallpeak



Joined: 19 Dec 2013
Posts: 6
tallpeak 21 Dec 2013, 07:01
Speaking of which, here's that little timing app

;; time_fbcd.asm
;; By Aaron W. West 2013-12-20 tallpeak@hotmail.com

; timing is consistently around 216 for fbstp regardless of the size of the number (not including printf %x conversion)
; timing is around 230 for div for 9 digits or 460+ for 18 digits
; timing is around 42 to 76 cycles for utoa_mul with 9 to 18 digits
; timing is around 230+ cycles for 9 digits for _itoa, similar to div loop
;
format PE64 console
entry start

include 'C:\dev\fasmw\INCLUDE\WIN64a.INC' ; win64a?


UPPERLIMIT = 10000000 ; add up to two 0s; you can compute up to a billion

section '.text' code readable executable

start:
;sub rsp,8*5 ; reserve stack for API use and make stack dqword aligned
mov [counter],21 ; iterations to run (one line per iteration)
lea rcx,[headerline] ; a header so that people can see what I'm timing
call [printf]
loop1:

; first time floating point bcd store and pop instruction
RDTSC
mov [T0H],edx
mov [T0L],eax

mov rax,1234567891 ; 18 digit max
mov qword[ildvalue],rax
fild qword[ildvalue]
fbstp tword[ildvalue]

RDTSC
mov [T1H],edx
mov [T1L],eax
mov rax,qword[T1L]
sub rax,qword[T0L]

lea rcx,[formattime_hex]
mov rdx,rax
movzx r8,word[ildvalue+8]
mov r9,qword[ildvalue]
call [printf]

; now time my utoa with multiply by reciprocal method

RDTSC
mov [T0H],edx
mov [T0L],eax

mov rax,123456789
lea rdi, [utoabuf+23]
call utoa_mul

RDTSC
mov [T1H],edx
mov [T1L],eax
mov rax,qword[T1L]
sub rax,qword[T0L]

lea rcx,[formattime_string]
mov rdx,rax
mov r8,rdi
call [printf]


; now time utoa with division method

RDTSC
mov [T0H],edx
mov [T0L],eax

mov rax,123456789
lea rdi, [utoabuf+23]
call utoa_div

RDTSC
mov [T1H],edx
mov [T1L],eax
mov rax,qword[T1L]
sub rax,qword[T0L]

lea rcx,[formattime_string]
mov rdx,rax
mov r8,rdi
call [printf]

; now time msvcrt _itoa
RDTSC
mov [T0H],edx
mov [T0L],eax

mov rcx,123456787
lea rdx,[utoabuf]
mov r8,10 ; number base
call [_itoa]

RDTSC
mov [T1H],edx
mov [T1L],eax
mov rax,qword[T1L]
sub rax,qword[T0L]

lea rcx,[formattime_string]
mov rdx,rax
lea r8,[utoabuf]
call [printf]

lea rcx,[newline]
call [printf]


dec [counter]
jnz loop1

lea rcx,[pressenter]
call [printf]
call [getchar]
call [ExitProcess]
retq

utoa_div:
mov rbx,rax ; digits in rbx
mov rcx, 10
mov [rdi],byte 0
u2a1:
dec rdi
mov rax, rbx
xor rdx, rdx
div rcx
mov rbx, rax
add rdx,'0'
mov [rdi], dl
or rbx,rbx
jnz u2a1
retq

; multiplication-by-inverse version
; divmod10 can be implemented in two multiplies, so let's try it
utoa_mul:
utoa:
mov rbx, rax
mov rcx, 0x199999999999999a ;Text.Printf.printf"%x"$(2^64+9)`div`10
mov rsi, 10
mov [rdi],byte 0
u2am1:
mov rax, rbx
mul rcx ; rax contains modulus in high bits, rdx = div
mov rbx, rdx ; save div back to rbx
mul rsi ; now rdx = modulus (last digit, 0 to 9)
or dl, '0'
dec rdi
mov [rdi], dl
or rbx, rbx
jnz u2am1
retq



section '.data' data readable writeable
;; 20 bytes per number because (length $ show $ 2^64) == 20
T0L dd 0
T0H dd 0
T1L dd 0
T1H dd 0
counter dq 100
utoabuf rb 24
ildvalue dt 0.0
formatdecimal db '%d',9,0
formatstring db '%s',9,0
formattime_string db '%3d %10s ',0
formattime_hex db '%3d %llx%llx ',0
headerline db 'RDTSC clock timings and output for integer to decimal routines',13,10
db 'Fbstp/output utoa_div/output utoa_mul/output itoa/output',13,10,0
newline db 13,10,0
pressenter db 'Press enter to continue.',0
_caption db 'Win64 assembly',0

section '.idata' import data readable
library kernel32, 'kernel32.dll', \
msvcrt, 'msvcrt.dll'
import kernel32, ExitProcess, 'ExitProcess'
import msvcrt, printf, 'printf', getchar, 'getchar', _itoa, '_itoa'

kernel_name db 'KERNEL32.DLL',0
user_name db 'USER32.DLL',0

_ExitProcess dw 0
db 'ExitProcess',0
_MessageBoxA dw 0
db 'MessageBoxA',0
Post 21 Dec 2013, 07:01
View user's profile Send private message Reply with quote
tallpeak



Joined: 19 Dec 2013
Posts: 6
tallpeak 21 Dec 2013, 07:03
and Euler#92, maybe this should be a separate post.

It all started when I watch a video about an implementation in Haskell.

Haskell is probably more worthy of learning for most people these days than assembler, but perhaps assembler is more fun. Especially when I get tired of trying to read academic papers about the type-system and category theory until my brain almost exploded, AGGHHH!!!!


;; euler92_console.asm
;; By Aaron W. West 2013-11-22 tallpeak@hotmail.com
;; Run successfully in Flat Assembler 1.70.03 on Windows 2008R2 x64
;; Modified from Hello.asm
;; See http://flatassembler.net/
;; Timing1: 12.4 billion ticks @ 2.66 Ghz Q9400 (about 4.7 seconds)
;; Timing2: 5 billion ticks Core i7 820QM (about 1.6 seconds)
;; caching first 568 sumSquareDigits timings:
;; 4.2 billion / 2.1 billion cycles (respectively)

; after adding two types of caches, down to 0.23 seconds (Q9400@2.66ghz on one core)
; still not a combinatorial/analytic but rather a brute-force approach

;; see http://msdn.microsoft.com/en-us/library/windows/hardware/ff561499(v=vs.85).aspx
;; for a list of x64 registers

;; Note that I mostly avoided stack operations.
;; Perhaps I should have used a few more registers, too, if I knew
;; about r8 to r15 before I started

;; also:
;; http://www.tortall.net/projects/yasm/manual/html/objfmt-win64-exception.html
;; "The registers RAX, RCX, RDX, R8, R9, R10, R11 are volatile and can be freely used by a called function without preserving their values"
;; (I violated this rule for RBX, RSI, RDI; oops)

; Example of 64-bit PE program
; this might work?http://pastebin.com/3kywjBDY

format PE64 console
entry start

include 'C:\dev\fasmw\INCLUDE\WIN64a.INC' ; win64a?


UPPERLIMIT = 10000000 ; add up to two 0s; you can compute up to a billion

section '.text' code readable executable

start:
;sub rsp,8*5 ; reserve stack for API use and make stack dqword aligned
push rbx
push rsi
push rdi
push r11
push r12
push r13
push r14
push r15
call computeEuler92
pop r15
pop r14
pop r13
pop r12
pop r11
pop rdi
pop rsi
pop rbx

lea rcx,[formatstring]
lea rdx,[_message]
push rcx
push rdx
call [printf]
pop rdx
pop rcx

;invoke WriteConsole,<invoke GetStdHandle,STD_OUTPUT_HANDLE>,hello,5,dummy,0

;mov r9d,0
;lea r8,[_caption]
;lea rdx,[_message]
;mov rcx,0
;call [MessageBoxA]

call [getchar]
mov ecx,eax

xor rcx,rcx
call [ExitProcess]
retq

;; input: n = rax
;; output: sum(square(digits(eax)))
; let mutable s = 0
; let mutable x = n
; while x > 0 do
; s <- s + square(x % 10)
; x <- x / 10
; s
sumSquareDigits:
mov rbx, rax ; n (digits)
xor rsi, rsi ; zero sum
mov rcx,10 ; divisor = 10
ssdl1:
xor rdx, rdx ; upper dividend = 0
mov rax, rbx ; dividend = remaining digits
div rcx ; extract a digit to rdx
mov rbx, rax ; save remaining digits
;mov rax, rdx ; multiplier=multiplicand=digit extracted(mod 10)
;mul rax ; digit**2
; saves about a billion cycles using a lookup table
add rsi, [sqdigits+rdx*8] ; sum
or rbx,rbx ; test
jnz ssdl1
mov rax,rsi ; return in rax
retq

;; per http://www.mathblog.dk/project-euler-92-square-digits-number-chain/
;; cache the first 568 sumSquares
;; this reduces the timing from 11 billion cycles (4.2 seconds)
;; to 4.2 billion cycles (1.6 seconds) on Q9400
;; merely the speed of the non-memoized/non-cached scala!
;; cached scala was below a second!
makeSSDcache:
mov rdi, 0
msc1:
mov rax, rdi
call sumSquareDigits
mov [ssdcache+rdi*8],rax
inc rdi
cmp rdi, 1000
jl msc1
retq
;;
ssd2: cmp rax, 1000
jae ssd3 ;; sumSquareDigits
mov rax, [ssdcache+rax*8]
retq
ssd3: mov rcx, 1000
xor rdx,rdx
div ecx ; rdx = remainder, 0 to 999
mov r8, [ssdcache+rdx*8]
xor rdx,rdx
div ecx ;32-bit division is 2.2X faster!
mov rdx, [ssdcache+rdx*8] ; remainder
mov rax, [ssdcache+rax*8] ; quotient should be 0 to 10 because we only go to 10 million
add rax,rdx
add rax,r8
retq


;4189374bc6a7ef ; printf "%x"$ 2^64`div`1000

ssdmul:
; optimize division using multiplication by inverse
mov rcx, 004189374bc6a7f0h ; printf"%x"$toInteger$ceiling$2^64/1000
mul rcx ; rdx = div, rax*1000>>64 = remainder
push rax ; r8 = remainder, needs scaling
mov rax, rdx
mul rcx ; rdx=upper digits, rax=middle digits, need scaling
mov rbx, [ssdcache+rdx*8] ; lookup upper digits
mov rcx, 1000 ; for scaling
mul rcx ; rdx = middle digits
add rbx, [ssdcache+rdx*8]
pop rax
mul rcx
add rbx, [ssdcache+rdx*8]
mov rax,rbx
retq
; 8581146 in 68.4 to 71 million clock cycles @ 3487Mhz, or about 20ms

; let rec termination x = if x = 1 || x = 89 then x
; else termination (sumSquareDigits x)
termination:
t1:
cmp rax, 1
jz termdone
cmp rax, 89
jz termdone
call ssdmul ;; sumSquareDigits
jmp t1
termdone:
retq

clearSSDhistogram:
lea rdi,[ssdhist]
mov rcx,1000
;shr rcx,1
xor rax,rax
cld
rep stosq
retq

makeSSDhistogram_mul:
xor r9,r9
bh1:
mov rax, r9
;call ssd3 ; div: 201 to 205 million cycles
call ssdmul ; 68 to 70 million cycles
inc qword[ssdhist+rax*8]
inc r9
cmp r9, UPPERLIMIT
jl bh1
retq

; use nested loops to avoid division completely
; sumSquaredDigits(9876543) = ssd(9) + ssd(876) + ssd(543)
makeSSDhistogram_count:
push r9
push r10
push r11
mov r9,999 ; millions
shcm: mov r10,999 ; thousands
shct: mov r11,999 ; units
mov rbx, [ssdcache+r9*8]
add rbx, [ssdcache+r10*8]
shcu: mov rax,rbx
add rax,[ssdcache+r11*8]
inc qword[ssdhist+rax*8]
sub r11,1
jnc shcu
sub r10,1
jnc shct
sub r9,1
jnc shcm
pop r11
pop r10
pop r9
retq
; 1.65 billion clocks for 1 billion using this procedure (~480ms), versus 5.5 seconds in a Scala worksheet
; 16.7 million clocks for 10 million using this proc (~5ms)

sumSSDhistogram:
mov rdi,999 ;table index counter
xor r9,r9 ; sum
sumh1:
mov rax, rdi
call termination
cmp rax,89
jnz sumhnot89
add r9,[ssdhist+rdi*8]
sumhnot89:
dec rdi
jnz sumh1
mov rax,r9
retq

;pure brute-force
;// 4.5 seconds
;let countT89 () =
; let mutable count = 0 in
; for i = 1 to 10000000 do
; if (termination i) = 89 then
; count <- count + 1
; else ()
; count
;countT89:
; mov qword [ct89], 0
; mov qword [c89loop], 1
;c89l:
; mov rax, [c89loop]
; call termination
;
; cmp rax, 89
; jnz not89
; inc qword [ct89]
;not89:
; inc qword[c89loop]
; cmp qword[c89loop], 10000000
; jl c89l
; mov rax, [ct89]
; retq

;; compute the value then convert to decimal in the buffer
computeEuler92:
RDTSC
mov [T0H],edx
mov [T0L],eax
call makeSSDcache
;call countT89
call clearSSDhistogram
;call makeSSDhistogram_div
;call makeSSDhistogram_mul
call makeSSDhistogram_count
call sumSSDhistogram
push rax
RDTSC
mov [T1H],edx
mov [T1L],eax
pop rax
lea rdi,[_messageRest] ; end of buffer
call utoa

;this is the hard way:
;mov eax,[T1L]
;mov edx,[T1H]
;sub eax,[T0L]
;sbb edx,[T0H]
;; for some strange reason, I couldn't seem to just "and rax, 0ffffffffh" with this assembler...
;; see http://board.flatassembler.net/topic.php?p=101848
;shl rax, 32
;shr rax, 32
;"In 64-bit mode, all operations on 32-bit registers clear the upper dword"
;mov eax,eax
;shl rdx, 32
;or rax, rdx

;This is the easy way:
mov rax,qword[T1L]
sub rax,qword[T0L]

lea rdi,[TSCdisplayend]
call utoa
retq

utoa_div:
mov rbx,rax ; digits in rbx
mov rcx, 10
u2a1:
dec rdi
mov rax, rbx
xor rdx, rdx
div rcx
mov rbx, rax
add rdx,'0'
mov [rdi], dl
or rbx,rbx
jnz u2a1
retq

; multiplication-by-inverse version
; divmod10 can be implemented in two multiplies, so let's try it
utoa_mul:
utoa:
mov rbx, rax
mov rcx, 0x199999999999999a ;Text.Printf.printf"%x"$(2^64+9)`div`10
mov rsi, 10
u2am1:
mov rax, rbx
mul rcx ; rax contains modulus in high bits, rdx = div
mov rbx, rdx ; save div back to rbx
mul rsi ; now rdx = modulus (last digit, 0 to 9)
or dl, '0'
dec rdi
mov [rdi], dl
or rbx, rbx
jnz u2am1
retq


; some utoa routines I was considering are here:
;http://www.df.lth.se/~john_e/gems/gem003e.html
;http://computer-programming-forum.com/46-asm/7aa4b50bce8dd985.htm


section '.data' data readable writeable
;; 20 bytes per number because (length $ show $ 2^64) == 20
dummy dq 1
formatstring db '%s',0
hello db 'hello',0
_caption db 'Win64 assembly euler#92',0
_message db 13,10,'The return value of Euler92 is:',9
_messageReturn db ' 0'
_messageRest db 13,10,'TSC=',9,' 0'
TSCdisplayend db 13,10,'Divide TSC by your clock frequency to compute elapsed time'
db 13,10,13,'Thank you for your time!',0

sqdigits dq 0,1,4,9,16,25,36,49,64,81
ct89 dq 0
c89loop dq 0
T0L dd 0
T0H dd 0
T1L dd 0
T1H dd 0
ssdcache rq 1000 ; ssd [0..999] fits in bytes
ssdhist rq 1000 ; histogram to store count of sumSquareDigits for 1..1000000



section '.idata' import data readable
;section '.idata2' data readable import
library kernel32, 'kernel32.dll', \
msvcrt, 'msvcrt.dll'
import kernel32, ExitProcess, 'ExitProcess'
import msvcrt, printf, 'printf', getchar, 'getchar'



kernel_name db 'KERNEL32.DLL',0
user_name db 'USER32.DLL',0

_ExitProcess dw 0
db 'ExitProcess',0
_MessageBoxA dw 0
db 'MessageBoxA',0
Post 21 Dec 2013, 07:03
View user's profile Send private message Reply with quote
tallpeak



Joined: 19 Dec 2013
Posts: 6
tallpeak 21 Dec 2013, 07:16
I forgot to reply more specifically to some points...

The INT instruction really isn't used much anymore. It's a strange special-purpose instruction which was invented to be a tiny system-call (or "syscall") which only takes 1 or 2 bytes (1 byte for int3, the interrupt instruction). Modern programs on 32-bit and 64-bit OSes don't use INT instructions, as far as I know, except perhaps in the BIOS. DOS used to, but who runs DOS anymore? The way it works is INT x looks up the address to jump to in a table stored in the first 0x400 bytes of memory (4 bytes x 256 entries; each entry has a segment and an offset for the real-mode memory-model.) But if you don't want to learn DOS or BIOS programming, it's quite irrelevant now.

Also, when I learned assembly, I studied the manual page-by-agonizing-page to understand every instruction in detail. I think you have to start by studying the instructions carefully and understanding every effect they have on the registers, memory and flags, and experiment with those instructions to see their effects. That takes time. DOS debug was a convenient tool. Now you can just get instant feedback with FASMW when you press F9 and I guess that's just as good as DOS debug, especially when you mix in some printf calls. As long as you know the register calling convention for printf in 64-bit mode.... (rcx, rdx, r8, r9.... are for the first 4 arguments).
Post 21 Dec 2013, 07:16
View user's profile Send private message Reply with quote
HaHaAnonymous



Joined: 02 Dec 2012
Posts: 1178
Location: Unknown
HaHaAnonymous 21 Dec 2013, 14:25
[ Post removed by author. ]


Last edited by HaHaAnonymous on 28 Feb 2015, 19:00; edited 1 time in total
Post 21 Dec 2013, 14:25
View user's profile Send private message Reply with quote
tallpeak



Joined: 19 Dec 2013
Posts: 6
tallpeak 21 Dec 2013, 16:42
Also, I would learn instructions such as these first:

load/store:
MOV
MOVSX
MOVZX
XCHG (but I don't use it often)
PUSH
POP

arithmetic:
ADD, SUB, DIV, MUL, SHL, SHR, SAR (sign-extending shift)
INC, DEC
AND, OR, XOR, NOT, NEG
CMP (same as sub but no effect on the destination, just the flags)
LEA, which is sometimes used for arithmetic instead of loading the address of an operand

Control-flow:
JMP, JA/JNC, JB/JC,, JGT, JLT, JEQ, etc
CALL, RET, RET n (drops n bytes from the stack *after* popping the return address, if I remember correctly, but I haven't used it lately.

You can learn the "important" instructions partly by studying example code, but be sure and simplify it and write your own code so that you know more intimately what is happening. I guess it could help if you found highly-commented code, but that's something I don't tend to do on the instruction-level very often.

I'm not really an assembly coder, but it's rather fun to do and a refreshing break from "real" work that I do (which tends to be database-related.)

It would be nice if you could start with a simpler instruction-set, such as the TMS9900 I started on with only 74 instructions, or a 6502 (VIC-20) I experimented with later. It would be great if there's an emulator somewhere that lets you time in code, assemble it, then single-step through it seeing the change in the registers and flags. You can do that sort of thing with GDB and perhaps DDD as an interface to GDB (I don't recall using dDD). I liked Borland Turbo Debugger... sigh...
Post 21 Dec 2013, 16:42
View user's profile Send private message Reply with quote
Hugh Aguilar



Joined: 15 Nov 2011
Posts: 62
Location: Arizona
Hugh Aguilar 06 Jan 2014, 05:33
bitRAKE wrote:
Challenge yourself with puzzles...

Well, I'll add my two cents worth.

I don't like to study, because studying is boring, and little or nothing sinks in to my brain.

I highly recommend learning to program in a new language by writing a program in that language. It doesn't have to be useful. Start out with something easy and fun (puzzles are a good challenge, as bitRAKE said). Just dive into writing it! As you go, you will have to look up a lot of info on the language, and this is how you will learn --- but you have to look up info because you need it right away, rather than because you have a vague expectation that the info will someday be useful --- using the info right away helps it to sink into your brain. Your first program will likely be inefficient and unidiomatic, but just make sure that it actually works --- then ask experts here to criticize it, and tell you how it is inefficient and unidiomatic --- then rewrite it to be better. Pretty soon your program will be as well-written as any expert could have done --- then you start over with a more difficult program --- repeat until you feel confident that you can write any program.

bitRAKE wrote:
Debuggers are a GREAT way to learn - seeing what each instruction does while stepping through code -- I still use this method every day.


Contrary to popular opinion, assembly-language is actually easier to learn and easier to program in, than high-level languages! The reason is that it is possible to know exactly what is going on (the debugger shows you exactly what is going on, as bitRAKE said). The assembly instructions are all simple and easy to understand --- by comparison, with a language such as C++, there is a lot going on under the hood and only the compiler-writers know for sure what exactly, but everybody else just has an vague guess as to the inner-workings.

Forth is similar to assembly-language in that it is understandable. It is possible to execute functions on the command-line and just look at the result on the stack afterward. Interactivity is a huge help in learning! With Forth you have a stack, and with assembly-language you have registers, but either way it is possible to examine the result of any instruction and see exactly what happened. Forth is pretty much just an overgrown macro-assembler, so it shouldn't be surprising that I see a lot of similarity between Forth and assembly-language. If you should decide to learn Forth, you can use my novice package: http://www.forth.org/novice.html

As a bit of history. Back in the mid 1990s, I tried to learn C++ and found it baffling. I got bogged down in terms such as "multiple-inheritance" and "polymorphism" that I didn't understand, and the books all used analogies (such as the platypus for multiple-inheritance), but they didn't really define the terms. I was about to despair, when I decided to start studying C++ compiler-writing. This seemed like a big jump, considering that I hadn't written a single C++ program yet. Suddenly however, C++ made sense! When I saw the assembly-language generated, I could understand it. The VMT (virtual-method-table) was really not complicated at all. Also, I was already familiar with a lot of concepts, such as inheritance, from my own assembly-language programming --- although I hadn't known the terminology before.

For more history, I got hired as an IBM370 assembly-language programmer without having ever seen an IBM370 and without knowing anything about the subject. I just dived straight into writing programs (for customers, and with a deadline) as if I knew what I was doing, and I looked up info as I needed it. I did the same thing at another job with PIC16 assembly-language. If you already know a few assembly-languages, it is not difficult to learn new ones. You dive into the deep end of the pool and start swimming --- there is no other way to learn!

As a final note, I recommend learning 32-bit or 64-bit x86, but not 16-bit x86 (your mention of INT implies that you have been trying to learn 16-bit X86, as that is from MS-DOS days). The 16-bit x86 had a lot of restrictions ---- learning to work within those restrictions is actually more difficult than to learn on the bigger processors in which you can pretty much use any register with any instruction. If you learn 16-bit x86 first, you will later on tend to impose restrictions on the 32-bit and 64-bit x86 that aren't necessary.

Also, as Revolution said, learning Menuet as a way to learn assembly-language seems overly ambitious. I haven't learned Menuet myself, but I likely will eventually --- learning Menuet (or Linux, or any other OS) in detail is a pretty daunting project, and not something I'm looking forward to doing.

Good luck!
Post 06 Jan 2014, 05:33
View user's profile Send private message Send e-mail Reply with quote
x3k30c



Joined: 14 Aug 2013
Posts: 13
Location: Hungary
x3k30c 13 Jan 2014, 00:42
"but you have to look up info because you need it right away, rather than because you have a vague expectation that the info will someday be useful"

- this is a great advice! and debuggers can also be useful
Post 13 Jan 2014, 00:42
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
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 vote in polls in this forum
You can attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.