flat assembler
Message board for the users of flat assembler.
Index
> MenuetOS > [Newbie question] Steps to learn ASM? |
Author |
|
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. |
|||
19 Oct 2010, 06:47 |
|
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... |
|||
19 Oct 2010, 15:28 |
|
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. |
|||
19 Oct 2010, 20:00 |
|
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 ! but more importantly ... understand why it works. |
|||
19 Oct 2010, 21:27 |
|
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
|
|||
19 Oct 2010, 23:19 |
|
S0urcerr0r 21 Oct 2010, 04:52
bitRAKE wrote: [plenty of useful info] BIG THANKS! 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! |
|||
21 Oct 2010, 04:52 |
|
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. |
|||
21 Dec 2013, 07:00 |
|
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 |
|||
21 Dec 2013, 07:01 |
|
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 |
|||
21 Dec 2013, 07:03 |
|
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). |
|||
21 Dec 2013, 07:16 |
|
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 |
|||
21 Dec 2013, 14:25 |
|
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... |
|||
21 Dec 2013, 16:42 |
|
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! |
|||
06 Jan 2014, 05:33 |
|
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 |
|||
13 Jan 2014, 00:42 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.