Visualizzazione post con etichetta Assembler. Mostra tutti i post
Visualizzazione post con etichetta Assembler. Mostra tutti i post

lunedì 23 luglio 2018

FIxed point math

Questo argomento a cui volevo dedicare un po' di tempo e  finalmente ho trovato degli esempi semplici  come ad esempio https://spin.atomicobject.com/2012/03/15/simple-fixed-point-math/

La logica di base e' semplice..si deve descrivere un numero a virgola mobile con un numero intero e per farlo lo si moltiplica per un valore fisso (una potenza di 2 nel caso di uso di calcolatore) ed eliminando la parte oltre la virgola.

Fixed = round(float * scale_fact)

Per scale_fact si usando potenze di due perche' e' semplice con lo shift dei byte ottenere il valore desiderato (1<< 4 = 16, 4 shift a sinistra per esempio)

Le operazioni di somma e sottrazione non sono modificate
La moltiplicazione invece diventa

r = (x * y)/(1<<scale_fact)

mentre la divisione

r=(x*(1<<scale_fact))/y

Tutto cio' velocizza i calcoli ma introduce necessariamente una approssimazione (in base al fattore di scala)

Proviamo a fare un esempio concreto
x = 3.141592655
y = 2
scale_fact = 16 = 65536

x1 = pi*65536 = 205321.92 = 205887
y1 = 2*65536 = 131072

r =  x1*y1 = 205887*131072 / 655536 = 411744

(si vede chiaramente che, dovendo applicare questo metodo ad un calcolatore, durante l'operazione si deve prevedere una variabile di tipo superiore al risultato....cerco di spiegarmi meglio...anche se gli operando sono entrambi byte ed il risultato e' un byte durante il calcolo e' necessario l'uso di una word)

riconvertendo il tutto
r = 411744/65536 = 6.2831726 (il valore dovrebbe essere 6.2831853...)

passando ad uno scale_fact = 8 (256)

x1 = pi*256 = 804
y1 = 2*256 = 512
r = 2526*512/256 = 1608

r = 1608/256 =6.28125 (il valore dovrebbe essere 6.2831853...)

passando ad uno scale_factr = 4 si ha un valore di x*y=6.25

in sintesi l'ordine dell' errore e'
16 bit : E-6
8 bit : E-4
4 bit  :  E-3




Moltiplicazione Assembly per C64

Il set di opcode del 6510 (processore del C64) non comprende in modo nativo l'operazione di moltiplicazione e divisione. Su codebase64 sono disponibili algoritmi per il calcolo e' ho provato ad usare quello piu' semplice (8 bit * 8 bit = 8 bit) usando sia la sintassi di Acme che di KickAss (anche come promemoria)
Il risultato viene salvato nell'accumulatore e quindi spostato in una variabile


KickAss
--------------
*=$c000

//sys 49152



lda #$00
  beq enterLoop

doAdd:
  clc
  adc num1

loop:
  asl num1
enterLoop: 
  lsr num2
  bcs doAdd
  bne loop

end:
sta num3
rts

num1: .byte 7
num2: .byte 3
num3: .byte 0

--------------


Acme
--------------
; Moltiplicazione 8bit*8bit=8bit
!source "./stdlib/stdlib.a"
!to "fixed.prg", cbm
!sl "fixed.map"
!cpu 6510
!ct pet


*=$c000

;sys 49152



lda #$00
  beq enterLoop

doAdd:
  clc
  adc num1

loop:
  asl num1
enterLoop: 
  lsr num2
  bcs doAdd
  bne loop

end:
sta num3
rts

num1: !by 7
num2: !by 3
num3: !by 0

mercoledì 8 giugno 2016

Mandelbrot su C64 in Assembler

Frugando su internet ho trovato la versione in Assembler di Mandelbrot per C64 compresa nel progetto Resurrection (sotto cartella Mandelbrot)

Il codice e' ben commentato ed il risultato e' in modalita' testo



Il tempo di calcolo su VICE in emulazione del tempo reale del C64 risulta essere di 90 secondi con 26 iterazioni per punto
Il codice e' compilabile con il crosscompiler ACME e necessita della libreria stdlib
----------------------------------------------------------------
;Mandelbrot test code

!source "../stdlib/stdlib.a"
!to "Mandelbrot.prg", cbm
!sal
!sl "Mandelbrot.map"
!svl "Mandelbrot.lbl"
!cpu 6510
!ct pet

!source "../stdlib/BASICEntry80d.a"

!macro OutputFAC {
jsr $bddd ; Convert FAC#1 to ASCII String. Kills FAC#2
jsr $b487 ; Set Up String
jsr $ab21 ; Output String
}

LDFAC = $bba2
STFAC = $bbd4
SUBFAC = $b850
DIVFAC = $bb0f
CMPFAC = $bc5b
MULFAC = $ba28
ADDFAC = $b867
SETFAC = $b391

!macro LoadFAC .addr {
lda #<.addr
ldy #>.addr
jsr LDFAC
}

!macro StoreFAC .addr {
ldx #<.addr
ldy #>.addr
jsr STFAC
}

!macro SubMemFAC .addr {
lda #<.addr
ldy #>.addr
jsr SUBFAC
}
!macro SubFACMem .addr {
+StoreFAC Temp
+LoadFAC .addr
+SubMemFAC Temp
}

!macro DivMemFAC .addr {
lda #<.addr
ldy #>.addr
jsr DIVFAC
}
!macro DivFACMem .addr {
+StoreFAC Temp
+LoadFAC .addr
+DivMemFAC Temp
}

!macro CmpFACMem .addr {
+StoreFAC Temp
+LoadFAC .addr
+CmpMemFAC Temp
}
!macro CmpMemFAC .addr {
lda #<.addr
ldy #>.addr
jsr CMPFAC
}

!macro MulMemFAC .addr {
lda #<.addr
ldy #>.addr
jsr MULFAC
}

!macro AddMemFAC .addr {
lda #<.addr
ldy #>.addr
jsr ADDFAC
}

!macro SetFAC_Y {
lda #0
jsr SETFAC
}

kMaxIterations = 26

!zn
*=BASICEntry
cld
jsr CINT


; Speed testing for some FP ops. As we can see even some simple operations are very slow
!if 0 {
sei
.tl1
lda #0
sta VIC2BorderColour
+MACROWaitForRaster 64
inc VIC2BorderColour

+LoadFAC xmin
inc VIC2BorderColour

+SubMemFAC xmax
inc VIC2BorderColour

+DivFACMem ScrW
inc VIC2BorderColour

+StoreFAC xs
inc VIC2BorderColour

+MulMemFAC xs
inc VIC2BorderColour

+StoreFAC xs
inc VIC2BorderColour

jmp .tl1
}

; The main Mandelbrot code.
;xs=(xmax-xmin)/40.0;
+LoadFAC xmin
+SubMemFAC xmax
+DivFACMem ScrW
+StoreFAC xs

;ys=(ymax-ymin)/24.0;
+LoadFAC ymin
+SubMemFAC ymax
+DivFACMem ScrH
+StoreFAC ys

;for (y=0;y<24;y++) {
lda #0
sta y

.l1
;   for (x=0;x<40;x++) {
lda #0
sta x

.l2
;      p=xmin+(x*xs);
ldy x
+SetFAC_Y
+MulMemFAC xs
+AddMemFAC xmin
+StoreFAC p


;      q=ymin+(y*ys);
ldy y
+SetFAC_Y
+MulMemFAC ys
+AddMemFAC ymin
+StoreFAC q


;         xtemp=0;
;         x0=0;
;         y0=0;
ldy #0
+SetFAC_Y
+StoreFAC x0
+StoreFAC y0


;         i=0;
lda #0
sta i

.while
;         while (((x0*x0)+(y0*y0))<4 && ++i<kMaxIterations)  {
; i check, first, simple integer check
lda i
cmp #kMaxIterations
bcc .ltkMaxIterations

jmp .escape
.ltkMaxIterations

; mul check
+LoadFAC x0
+MulMemFAC x0
+StoreFAC Temp2
+LoadFAC y0
+MulMemFAC y0
+AddMemFAC Temp2
+CmpFACMem Escape
bcc .lt4
jmp .escape
.lt4

;++i
inc i

; xtemp = (x0*x0) - (y0*y0) + p
+LoadFAC x0
+MulMemFAC x0
+StoreFAC Temp2
+LoadFAC y0
+MulMemFAC y0
+SubMemFAC Temp2
+AddMemFAC p
+StoreFAC xtemp

;            y0=(2 * x0 * y0) +q;
+LoadFAC x0
+MulMemFAC y0
+StoreFAC Temp2
ldy #2
+SetFAC_Y
+MulMemFAC Temp2
+AddMemFAC q
+StoreFAC y0


;            x0=xtemp;
+LoadFAC xtemp
+StoreFAC x0

;            }
jmp .while

.escape
; Range check to adjust for printable characters
lda i
; lsr ; Potential for shit adjustment, change kMaxIterations to accomodate
cmp #26 ; Letters in the alphabet!
bcc .okChar
lda #' '
jmp .rawOut
.okChar
clc
adc #64
.rawOut
jsr CHROUT

; Finish for loops
inc x
lda x
cmp #40
beq .ol2
jmp .l2
.ol2
inc y
lda y
cmp #24 ; MPi: TOOD: Keep the last line clear for an IRQ scrolling message
beq .ol1
jmp .l1
.ol1

.flash
; inc VIC2BorderColour
jmp .flash

; C64 Floating point values
; MPi: TODO: Must update the assembler to create these
xmin !by $82, $a0, $00, $00, $00 ; -2.5
xmax !by $81, $40, $00, $00, $00 ; 1.5
ymin !by $81, $c0, $00, $00, $00 ; -1.5
ymax !by $81, $40, $00, $00, $00 ; 1.5
ScrW !by $86, $20, $00, $00, $00 ; 40
ScrH !by $85, $40, $00, $00, $00 ; 24
Escape !by $83, $00, $00, $00, $00 ; 4

; Float storage
Temp = *
Temp2 = * + 5
xs = * + 10
ys = * + 15
p = * + 20
q = * + 25
xtemp = * + 30
x0 = * + 35
y0 = * + 40
; Bytes
x = * + 45
y = * + 46
i = * + 47
----------------------------------------------------------------



lunedì 6 giugno 2016

Moltiplicazione veloce per 10 in binario

Questo e' un sistema rapido per effettuare moltiplicazioni per 10 su un calcolatore binario ripresa da questo documento

L'idea e' quella di dividere la moltiplicazione per 10 come (numero*8)+(numero*2) che equivale ovviamento a numero numero*(8+2). Il vantaggio di questo procedimento e' che in con codificato binario le moltiplicazione per 2 (e per i suoi multipli) consistono semplicemente in uno spostamento verso sinistra dei bit.

Un esempio: moltiplichiamo 123x10
la codifica binaria di 123 e' 1111011
lo shift di un posto (moltiplicazione per due) e' 11110110
lo shift di tre posti (moltiplicazione per otto) e' 1111011000
la somma binaria di 11110110 + 1111011000 equivale a 10011001110 che corrisponde al risultato desiderato di 1230

Il vantaggio di questo sistema e' che in Assembler esiste un comodo apposito per lo shift binario a sinistra che e' ASL nel 6502 e SHL in x86

mercoledì 1 giugno 2016

Sviluppo assembler su C64

Mi e' presa la voglia di tornare alle origini e programmare in assembler sul MOS 6502 (o meglio sul MOS 6510 del Commodore 64). Ovviamente il computer fisico e' in qualche discarica (o spero recuperato) e cerco di usare l'emulatore VICE

In modo, piu' o meno tradizionale, dopo aver avviato l'emulatore, si deve impiegare un assemblatore (nel caso Turbo Assembler per C64...con una curiosa omonimia con TASM Borland per x86) caricando l'immagine di un disco (preventivamente scaricat) con File/Attach Disk Image/Disk 8
con LOAD"$",8 e successivo LIST si ottiene il contenuto del disco


Si carica l'assemblatore con LOAD"*",8,1 ed al READY si digita SYS36864 entrando nella schermata nera di editing. Questo e' il semplice programma di test che stampa a video una stringa


programma in sintassi TASM
--------------------------------------------------
* = $1000

!basic
    ldx #$00
loop  
    lda message,x
    sta $0400,x
    inx
    cpx #$04
    bne loop
    rts
message .text "luca"
--------------------------------------------------

Per compilare il programma si deve usare la combinazione BackArrow+3 ma BackArrow non esiste sulla tastiera PC; si tratta del tasto piu' in alto a sinistra al di sotto del tasto di ESC (che nella tastiera italiana e' il backslash o barra rovescia)




s per start e si avvia il programma. Per rientrare nella fase di editing e' sufficiente ridigitare SYS 36864


E' comunque piuttosto noioso procedere nel modo classico ed e' molto piu' comodo usare C64 Studio (una IDE con allegato ACME un cross compiler 6502) e che permette di eseguire il programma lanciando autonomamente VICE



La differenza sostanziale tra i due metodi e' che usando C64 Studio si deve scrivere un preambolo in Basic che lancia poi il codice Assembler (in pratica una sola linea di codice BASIC che indichi un SYS49152 per passare il controllo al codice assembler...tale preambolo e' ralizzato con le sole righe *=$0801 ovvero la locazione di memoria dove risiede il compilatore BASIC e !basic.. non risulta necessario impostare *=$01000)

programma in sintassi ACME. Da notare come viene dichiarata la stringa
--------------------------------------------------
;set program start address
* = $0801

!basic
    ldx #$00
loop  
    lda message,x
    sta $0400,x
    inx
    cpx #$04
    bne loop
    rts
message !scr "luca"

fra le altre cose con C64 Studio si puo' utilizzare anche direttamente Basic


venerdì 1 febbraio 2013

Mandelbrot in Assembler

Premettendo che niente del codice che segue e' frutto del mio lavoro ho raccolto (prima che si perdano) un po' di sorgenti per generare l'insieme di Mandelbrot in Assembler x86
Questa era una attivita' tipica del DOS quando si poteva avere accesso diretto in scrittura sulla memoria della VGA...con i nuovi sistemi operativi tale approccio e' sostanzialmente impossibile



i compilatori sono vari (TASM, FASM) e si vede anche la differenza tra la presenza e l'assenza del coprocessore matematico (che nei primi processori x86 era opzionale)

Listato 1
--------------------------------

; Mandelbrot Set - fasm example program

; requires FPU

 org 100h

 mov ax,13h
 int 10h
 push 0A000h
 pop es

 mov dx,3C8h
 xor al,al
 out dx,al
 inc dl
 mov cx,64
    vga_palette:
 out dx,al
 out dx,al
 out dx,al
 inc al
 loop vga_palette

 xor di,di
 xor dx,dx
 finit
 fld [y_top]
 fstp [y]
screen:
 xor bx,bx
 fld [x_left]
 fstp [x]
   row:
 finit
 fldz
 fldz
 mov cx,63
    iterate:
 fld st0
 fmul st0,st0
 fxch st1
 fmul st0,st2
 fadd st0,st0
 fxch st2
 fmul st0,st0
 fsubp st1,st0
 fxch st1
 fadd [y]
 fxch st1
 fadd [x]
 fld st1
 fmul st0,st0
 fld st1
 fmul st0,st0
 faddp st1,st0
 fsqrt
 fistp [i]
 cmp [i],2
 ja over
 loop iterate
    over:
 mov al,cl
 stosb
 fld [x]
 fadd [x_step]
 fstp [x]
 inc bx
 cmp bx,320
 jb row
 fld [y]
 fsub [y_step]
 fstp [y]
 inc dx
 cmp dx,200
 jb screen

 xor ah,ah
 int 16h
 mov ax,3
 int 10h
 int 20h

x_left dd -2.2
y_top dd 1.25

x_step dd 0.009375
y_step dd 0.0125

x dd ?
y dd ?

i dw ?

--------------------------------

Listato 2
--------------------------------
;value: EAX - sentinella (max. TIEFE, min. 0)
PUBLIC rechnePunkt_
rechnePunkt_ PROC NEAR
TIEFE EQU DWORD PTR [EBP + 24]
CI_S EQU QWORD PTR [EBP + 16]
CR_S EQU QWORD PTR [EBP + 8]

push ebp
mov ebp, esp

fld CI_S
fstp ci
fld CR_S
fstp cr

push ecx
mov ecx, TIEFE

; CoProzessorstack einrichten
fldZ ; z.r = 0
fldZ ; z.i = 0
fldZ ; z.r^2 = 0
fldZ ; z.i^2 = 0

l1:
;Jetzt: st(0) - (z.i)^2
; st(1) - (z.r)^2
; st(2) - z.i
; st(3) - z.r

fsubp st(1),st ; z.r^2 - z.i^2
fadd cr ; z.r^2 - z.i^2 + c.r
fstp st(3)

fmulp st(1),st ; z.r * z.i
fadd st,st ; 2 * z.r * z.i
fadd ci ; 2 * z.r * z.i + c.i

;Ora : st(0) - new z.i = z.i
; st(1) - new z.r = z.r

fld st(1) ; z.r
fmul st,st ; z.r^2
fld st(1) ; z.i
fmul st,st ; z.i^2

fld st(1) ; z.r^2
fadd st,st(1) ; z.r^2 + z.i^2

fcomp grenze ; st = zabs2, < 4.0 ?

;Ora : st(0) - (z.i)^2 (confronta con occupazione in l1:)
; st(1) - (z.r)^2
; st(2) - z.i
; st(3) - z.r

fstsw ax
sahf
jae ende ;quanso st >= 4.0 -> vai a ende

dec ecx ;piu' veloce di un ciclo
jnz l1

ende: ffree st(3)
ffree st(2)
ffree st(1)
ffree st

mov eax, TIEFE
sub eax, ecx

pop ecx

mov esp, ebp
pop ebp
ret 20

rechnePunkt_ ENDP

_TEXT ENDS

--------------------------------

Listato 3
--------------------------------
; Mandelbrot found in the FIDO-Net. Very nice.
org 100h
start: mov ax,13h
int 10h ; set 320x200 256 color mode
mov ax,0a000h
mov ds,ax ; load ds with video segment
xor di,di ; zero out screen offset
mov bp,-200 ; load y with -(screen height)
l1: mov si,-320 ; load x with -(screen width)
l2: push di ; save screen offset
xor bx,bx ; val1 = 0
xor di,di ; val2 = 0
l3: push bx
lea ax,[bx+di] ; ax = val1 + val2
sub bx,di ; bx = val1 - val2
imul bx ; ans = val1^2 - val2^2
mov bl,ah
mov bh,dl
lea bx,[bx+si-64] ; val1 = (ans/256) + x - 64
pop ax
imul di ; ans = val1 * val2
mov al,ah
mov ah,dl
add ax,ax
xchg ax,di
lea di,[bp+di-56] ; val2 = (ans/128) + y - 56
cmp bh,4
jg draw ; if val1 > 1024 then draw point
inc cl ; increment color
jne l3
draw: pop di ; restore screen offset
xchg [di],cl ; store color, and make color = 0
inc di ; increment screen offset
inc si ; increment x
jne l2 ; if x <> 0 then continue lp2
inc bp ; increment y
jne l1 ; if y <> 0 then continue lp1
xor ax,ax
int 16h
mov ax,3
int 10h ; set text mode
ret ; exit program

--------------------------------




lunedì 5 novembre 2012

Primi passi in Assembler su Linux


piu' che altro per divertimento ho provato a riscrivere qualcosa in assembler..era dai tempi del DOS e di Int 21h che non usavo piu' l'assembler
E' curioso ritrovare che anche Linux come Dos usa un interrupt (80h) per la gestione del sistema ed e' anche curioso di come si possano usare le chiamate alle funzioni C (in questo caso printf) dall'Assembler (di solito e' il linguaggio di piu' alto livello che sfrutta quello di livello piu' basso)

Tutti gli esempi sono compilati con nasm

Il primo esempio stampa una scritta a video usando l'Int 80h
Per la compilazione si devono dare i seguenti comandi
nasm -f elf32 scrivi.asm
ld -o scrivi scrivi.o

scrivi.asm
-------------------------------------------------------
global _start

section .text
_start:
    mov eax,4
    mov ebx,1
    mov ecx,messaggio
    mov  edx,lunghezza
    int 80h

    mov eax,1
    mov ebx,0
    int 80h

section .data
    messaggio: db 'Hello ',0x0a
    lunghezza: equ $-messaggio


-------------------------------------------------------
come output si ha
Hello

Per il secondo esempio si usa invece la chiamata esterna printf per stampare il valore di una variabile

In questo caso la catena di compilazione e' differente per venire incontro alla chiamata esterna che e' risolta da gcc
nasm -f elf32 -l scrivi2.lst scrivi2.asm
gcc -o scrivi2 scrivi2.o

scrivi2.asm
-------------------------------------------------------
 extern printf

section .text

    global main
main:
    push ebp
    mov ebp,esp


    push dword [a]
    push dword formato
    call printf

    add esp,12

    mov esp,ebp
    pop ebp

    mov eax,0
    ret

section .data
    a: dd 5
    formato: db "a=%d",10,
0

-------------------------------------------------------
come output si ha
a=5

Molto simile al precedente il terzo esempio mostra un ciclo

nasm -f elf32 -l scrivi3.lst scrivi3.asm
gcc -o scrivi3 scrivi3.o

scrivi3.asm
-------------------------------------------------------
 extern printf

section .text

    global main
main:
    push ebp
    mov ebp,esp

    mov ebx,10
gira:
    push ebx
    push dword formato
    call printf
    dec ebx
    jnz gira
  

    mov esp,ebp
    pop ebp

    mov eax,0
    ret

section .data
    formato: db "ciclo=%d",10,0

-------------------------------------------------------

produce come output
ciclo=10
ciclo=9
ciclo=8
ciclo=7
ciclo=6
ciclo=5
ciclo=4
ciclo=3
ciclo=2
ciclo=1





Geologi

  E so anche espatriare senza praticamente toccare strada asfaltata