~vdupras/duskos

4d28f512ef3a22445a37d5f3abd55af351b9b0d6 — Virgil Dupras 7 days ago d04ddb2
asm/arm: rewrite to get rid of global states

see doc/asm/arm
M fs/asm/arm.fs => fs/asm/arm.fs +46 -80
@@ 1,91 1,57 @@
\ ARM assembler
?f<< /lib/math.fs
?f<< /lib/endian.fs
?f<< /asm/label.fs

: ,) ( op -- ) le, ;

0 const r0         1 const r1          2 const r2          3 const r3
4 const r4         5 const r5          6 const r6          7 const r7
8 const r8         9 const r9          10 const r10        11 const r11
12 const r12       13 const r13        14 const r14        15 const r15
11 const rFP
12 const rIP       13 const rSP        14 const rLR        15 const rPC

14 ( AL ) value curcond \ current condition flag
0 value shift \ contains the whole "shift" field (8 bits)
-1 value imm \ 32-bit value. will be shifted later
0 value setcond \ set condition flag
\ those 2 below are only for LDR and STR
0 value sdtflags \ I P U B W
0 value sdtoff

: cond doer , does> @ to curcond ;
0 cond EQ          1 cond NE           2 cond CS           3 cond CC
4 cond MI          5 cond PL           6 cond VS           7 cond VC
8 cond HI          9 cond LS           10 cond GE          11 cond LT
12 cond GT         13 cond LE

\ immediate shift operations
: lsl) 3 lshift to shift ;
: lsr) 3 lshift 2 or to shift ;
: asr) 3 lshift 4 or to shift ;
: ror) 3 lshift 6 or to shift ;
\ register shift operations
: rlsl) 4 lshift 1 or to shift ;
: rlsr) 4 lshift 3 or to shift ;
: rasr) 4 lshift 5 or to shift ;
: rror) 4 lshift 7 or to shift ;

: i) to imm ;
: s) 1 to setcond ;
: imm? imm -1 <> ;
: asm$
  14 ( AL ) to curcond 0 to shift -1 to imm 0 to setcond 0 to sdtflags
  0 to sdtoff ;
: op, ( op -- ) curcond 28 lshift or , asm$ ;
: oprdrn, ( rd rn op -- )
  swap 16 lshift or swap 12 lshift or ( opcode ) op, ;

\ Data processing ops
: shiftedimm $10 imm begin ( shift n )
  dup $100 >= while
  dup 1 and if abort" invalid immediate value" then
  >> >> swap 1- swap repeat ( shift n ) swap 8 lshift or ;
: _ ( rd rop1 rop2? opcode -- )
  21 lshift setcond 20 lshift or
  imm? if ( rd rop1 opcode )
    shiftedimm or $02000000 or
  else ( rd rop1 rop2 opcode )
    shift $ff and 4 lshift or or then ( rd rop1 opcode ) oprdrn, ;

: op ( opcode -- ) doer , does> @ _ ;
0 op and,          1 op eor,           2 op sub,           3 op rsb,
4 op add,          5 op adc,           6 op sbc,           7 op rsc,
12 op orr,         14 op bic,

\ TST TEQ CMP CMN have no destination register and s) is implicit
: op ( opcode -- ) doer , does> @ s) imm? if 0 rot> else >r 0 rot> r> then _ ;
8 op tst,          9 op teq,           10 op cmp,          11 op cmn,

\ MOV and MVN only have one operand
: op ( opcode -- ) doer , does> @ 0 imm? if swap else rot> then _ ;
13 op mov,         15 op mvn,

: rn) ( op r -- op ) 16 lshift or ;
: rd) ( op r -- op ) 12 lshift or ;
: rm) ( op r -- op ) or ;

\ immediate shift operations ( op n -- op )
: _ ( op n type -- op ) dip 3 lshift | << or 4 lshift or ;
: lsl) 0 _ ; : lsr) 1 _ ; : asr) 2 _ ; : ror) 3 _ ;
\ register shift operations ( op r -- op )
: _ ( op r type -- op ) dip 4 lshift | << 1+ or 4 lshift or ;
: rlsl) 0 _ ; : rlsr) 1 _ ; : rasr) 2 _ ; : rror) 3 _ ;

: cond doer , does> @ 28 lshift swap $0fffffff and ;
0 cond eq)         1 cond ne)          2 cond cs)          3 cond cc)
4 cond mi)         5 cond pl)          6 cond vs)          7 cond vc)
8 cond hi)         9 cond ls)          10 cond ge)         11 cond lt)
12 cond gt)        13 cond le)

: op doer , does> @ 21 lshift $e0000000 or ;
0 op and)          1 op eor)           2 op sub)           3 op rsb)
4 op add)          5 op adc)           6 op sbc)           7 op rsc)
8 op tst)          9 op teq)           10 op cmp)          11 op cmn)
12 op orr)         13 op mov)          14 op bic)          15 op mvn)

: #? ( n -- immop rest )
  r! dup if log2 then 7 - max0 dup 1 and if 1+ then ( shift ) \ V1=n
  r@ over rshift ( shift b )
  2dup swap lshift r> -^ ( shift b rest )
  rot neg $1e and 7 lshift rot or ( rest imm ) swap ;

: i) ( op n -- op )
  #? if abort" invalid immediate value" then or $02000000 or ;

\ Single Data Transfer (LDR, STR)

: pre) sdtflags $8 or to sdtflags ;
: post) sdtflags $17 and to sdtflags ;
: +) to sdtoff sdtflags $4 or to sdtflags ;
: -) to sdtoff sdtflags $1b and to sdtflags ;
: +i) sdtflags $f and to sdtflags +) ;
: -i) sdtflags $f and to sdtflags -) ;
: +r) sdtflags $10 or to sdtflags +) ;
: -r) sdtflags $10 or to sdtflags -) ;
: 8b) sdtflags $2 or to sdtflags ;
: !) sdtflags $1 or to sdtflags ;

: op ( lflag -- ) doer , does> @ ( rd rn lflag )
  20 lshift sdtflags 21 lshift or $04000000 or sdtoff or ( rd rn opcode )
  sdtflags $10 and if \ is a register offset
    shift $ff and 4 lshift or then ( rd rn opcode )
  oprdrn, ;

0 op str,          1 op ldr,
$e4000000 const str)         $e4100000 const ldr)

: pre) $01000000 or ;
: post) $feffffff and ;
: 12b# ( n -- ) $fffff000 and if abort" invalid offset" then ;
: +i) ( op n -- op ) dup 12b# or $00800000 or ;
: -i) ( op n -- op ) dup 12b# or $ff7fffff and ;
: +r) ( op r -- op ) +i) $02000000 or ;
: -r) ( op r -- op ) -i) $02000000 or ;
: 8b) $00400000 or ;
: !) $00200000 or ;

A fs/doc/asm/arm.txt => fs/doc/asm/arm.txt +73 -0
@@ 0,0 1,73 @@
# ARM assembler

ARM instructions all encode to a 32-bit number and the way these encoding work
incite us to deviate a little bit from the general Dusk assembler mechanism:
Instead of pushing arguments to PS and then call the "operation writer" word,
we instead begin the the operation mnemonic and "accumulate" arguments into it.
When all arguments are accumulated, the number of PS is exactly what we want to
write, which we can do with "le,", of which ",)" is an alias.

For example, to write a "add" instruction with r4 as a destination (Rd), r5 as
the first operand (Rn) and 42 as an immediate, we would do:

    add) r4 rd) r5 rn) 42 i) ,)

Symbol-wise, ")" means "accumulate", where the final ",)" means "write what has
been accumulated".

## Generic words

All operations work on registers and they pretty much all have a destination
register and one or two operand registers. Each register has a constant word:

    r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15
    rFP rIP rSP rLR rPC

These constants are meant to be used in conjunction with these accumulator
words:

    rd) Destination register
    rn) First operand register
    rm) Second operand register

Whenever there's a second operand register, the barrel shifter can be configured
with these words which all have the ( op n -- op ) signature, "op" being the
accumulated operation:

    lsl) lsr) asr) ror)     n = shift by "n" bits
    rlsl) rlsr) rasr) rror) n = register id containing the amount to shift by

Immediates replace the rm) operand and can be specified with the i) word. Shift
encoding is automatically performed by i) and will abort with an error message
if the specified immediate can't be encoded.

All operations can be conditionally executed. This condition is activated by one
of these words:

    eq) ne) cs) cc) mi) pl) vs) vc) hi) ls) ge) lt) gt) le)

## Data processing instructions

All those instructions except mov) and mvn) have 3 operators:

    and) eor) sub) rsb) add) adc) sbc) rsc)
    tst) teq) cmp) cmn) orr) mov) bic) mvn)

## Single Data Transfer

The str) and ldr) operations only use rd) and rn), with rd) being the register
containing the value to store or the register being the target for the load
operation. rn) is the register containing the target adress for the store/load
operation.

Many options come with those two operations and they are enabled with those
words:

    +i)   op n -- op  Add offset "n" to rn)
    -i)   op n -- op  Subtract offset "n" from rn)
    +r)   op r -- op  Add offset in register "r" to rn)
    -r)   op r -- op  Subtract offset in register "r" from rn)
    pre)  op -- op    Add offset before transfer
    post) op -- op    Add offset after transfer (default)
    8b)   op -- op    Load/Store operation is 8-bit
    !)    op -- op    Write effective address back to rn)

A fs/doc/hw/arm/arch.txt => fs/doc/hw/arm/arch.txt +3 -0
@@ 0,0 1,3 @@
# ARM architecture

TODO

A fs/doc/hw/arm/rpi/rpi.txt => fs/doc/hw/arm/rpi/rpi.txt +10 -0
@@ 0,0 1,10 @@
# Raspberry Pi

The main source of information for Raspberry Pi development is from the
Raspberry Pi foundation itself[1].

This Git repository contains, for example, the BCM2835 technical documentation,
which is the main document against which Dusk's first foray into the ARM world
(Raspberry Pi model 1) is built.

[1]: https://github.com/raspberrypi/documentation.git

M fs/lib/endian.fs => fs/lib/endian.fs +5 -1
@@ 5,10 5,12 @@ $1234 here ! here c@ $34 = const LITTLE_ENDIAN?

alias @ le@ :16b 16b @ ;
alias ! le! :16b 16b ! ;
alias , le, :16b 16b , ;
LITTLE_ENDIAN? not [if] abort" TODO" [then]

alias @ be@ :16b 16b @ ;
alias ! be! :16b 16b ! ;
alias , be, :16b 16b , ;
LITTLE_ENDIAN? [if]
  code be@ ( a -- n )
    W>A, A) 8b) @, 8 i) <<,


@@ 29,4 31,6 @@ LITTLE_ENDIAN? [if]
  code16b
    W>A, drop, A) 1 +) 8b) !, 8 i) >>,
    A) 8b) !, drop, exit,
[then]
\ No newline at end of file

  : be, here be! 4 allot ; :16b here 16b be! 2 allot ;
[then]

M fs/tests/asm/arm.fs => fs/tests/asm/arm.fs +6 -9
@@ 3,15 3,12 @@
ARCH S" i386" s= [if]
  ." ARM assembler messes up i386 asm, skipping" nl> \s [then]
?f<< /asm/arm.fs
\ We don't have an ARM port yet, we can't actually test. For now, all we do is
\ spit binary contents of our ops for manual disassembly checks.
: spit ( a u -- ) swap >r for 8b to@+ V1 .x1 next rdrop ;
testbegin
\ Tests for asm/arm.fs
code foo1
  r0 42 i) mov, \ mov r0, #42
  r1 r2 r3 +r) pre) 1 lsr) ldr,    \ ldr r1, [r2, r3 lsr #1]
  r3 $3f0000 i) mov, \ mov r3, #3f0000

current here over - spit
mov) r0 rd) 42 i)
  $e3a0002a #eq \ mov r0, #42
ldr) r1 rd) r2 rn) r3 +r) pre) 1 lsr)
  $e79210a3 #eq \ ldr r1, [r2, r3 lsr #1]
mov) r3 rd) $3f0000 i)
  $e3a039fc #eq \ mov r3, #3f0000
testend

M fs/xcomp/arm/rpi/kernel.fs => fs/xcomp/arm/rpi/kernel.fs +17 -14
@@ 27,36 27,39 @@ $8c const UART0_TDR

$8000 to binstart
0 align4 here to org
r0 MMIO_BASE i) mov, \ r0 = MMIO_BASE
r0 r0 GPIO_BASE i) add, \ r0 = GPIO_BASE
r2 r0 UART0_BASE i) add, \ r2 = UART0_BASE
mov) r0 rd) MMIO_BASE i) ,) \ r0 = MMIO_BASE
add) r0 rd) r0 rn) GPIO_BASE i) ,) \ r0 = GPIO_BASE
add) r2 rd) r0 rn) UART0_BASE i) ,) \ r2 = UART0_BASE
\ Disable UART0
r3 r2 UART0_CR i) add, r1 0 i) mov, r1 r3 str,
add) r3 rd) r2 rn) UART0_CR i) ,) mov) r1 rd) 0 i) ,) str) r1 rd) r3 rn) ,)
\ Disable pull up/down for all GPIO pins & delay for 150 cycles.
r3 r0 GPPUD i) add, r1 0 i) mov, r1 r3 str,
add) r3 rd) r0 rn) GPPUD i) ,) mov) r1 rd) 0 i) ,) str) r1 rd) r3 rn) ,)
\ TODO: add delay
\ Disable pull up/down for pin 14,15 & delay for 150 cycles.
r3 r0 GPPUDCLK0 i) add, r1 $c000 i) mov, r1 r3 str,
add) r3 rd) r0 rn) GPPUDCLK0 i) ,) mov) r1 rd) $c000 i) ,) str) r1 rd) r3 rn) ,)
\ TODO: add delay
\ Write 0 to GPPUDCLK0 to make it take effect.
r3 r0 GPPUDCLK0 i) add, r1 0 i) mov, r1 r3 str,
add) r3 rd) r0 rn) GPPUDCLK0 i) ,) mov) r1 rd) 0 i) ,) str) r1 rd) r3 rn) ,)
\ Clear pending interrupts.
r3 r2 UART0_ICR i) add, r1 $700 i) mov, r1 r1 $ff i) add, r1 r3 str,
add) r3 rd) r2 rn) UART0_ICR i) ,) mov) r1 rd) $700 i) ,)
add) r1 rd) r1 rn) $ff i) ,)       str) r1 rd) r3 rn) ,)
\ Set integer & fractional part of baud rate.
\ Divider = UART_CLOCK/(16 * Baud)
\ Fraction part register = (Fractional part * 64) + 0.5
\ Baud = 115200.
\ Divider = 3000000 / (16 * 115200) = 1.627 = ~1.
r3 r2 UART0_IBRD i) add, r1 1 i) mov, r1 r3 str,
add) r3 rd) r2 rn) UART0_IBRD i) ,) mov) r1 rd) 1 i) ,) str) r1 rd) r3 rn) ,)
\ Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40.
r3 r2 UART0_FBRD i) add, r1 40 i) mov, r1 r3 str,
add) r3 rd) r2 rn) UART0_FBRD i) ,) mov) r1 rd) 40 i) ,) str) r1 rd) r3 rn) ,)
\ Enable FIFO & 8 bit data transmission (1 stop bit, no parity).
r3 r2 UART0_LCRH i) add, r1 $70 i) mov, r1 r3 str,
add) r3 rd) r2 rn) UART0_LCRH i) ,) mov) r1 rd) $70 i) ,) str) r1 rd) r3 rn) ,)
\ Mask all interrupts.
r3 r2 UART0_IMSC i) add, r1 $700 i) mov, r1 r1 $f2 i) add, r1 r3 str,
add) r3 rd) r2 rn) UART0_IMSC i) ,) mov) r1 rd) $700 i) ,)
add) r1 rd) r1 rn) $f2 i) ,)       str) r1 rd) r3 rn) ,)
\ Enable UART0, receive & transfer part of UART.
r3 r2 UART0_CR i) add, r1 $300 i) mov, r1 r1 $01 i) add, r1 r3 str,
add) r3 rd) r2 rn) UART0_CR i) ,) mov) r1 rd) $300 i) ,)
add) r1 rd) r1 rn) $01 i) ,)       str) r1 rd) r3 rn) ,)


r3 r2 UART0_DR i) add, r1 'X' i) mov, r1 r3 str,
add) r3 rd) r2 rn) UART0_DR i) ,) mov) r1 rd) 'X' i) ,) str) r1 rd) r3 rn) ,)
$eafffffe , \ hardcoded "b -2"