;; See instructions.wat of soc_print_number test. A lot has been taken from ;; there. ;; Relevant addresses are VGA text memory (0xFFC00), VGA regs (0x100600), ;; lower half of timer reg (0x1BFC08) and led2 reg (0x1BFC06). (module (memory 0 2) ;; print number in decimal at address $dest, return address after ;; the last digit (func $print (param $number i32) (param $dest i32) (result i32) (local $adr1 i32) (local $adr2 i32) ;; remember the initial address to write at (set_local $adr1 (get_local $dest)) (loop $again ;; prepare address for store operation later (get_local $dest) ;; compute the last digit of number (i32.rem_u (get_local $number) (i32.const 10)) ;; 0x30 is ASCII encoding of digit 0 (i32.add (i32.const 0x30)) ;; write digit's ASCII char to memory (i32.store8 offset=0x0 align=1) ;; increment the pointer (set_local $dest (i32.add (i32.const 1) (get_local $dest))) ;; divide our number by 10 (set_local $number (i32.div_u (get_local $number) (i32.const 10))) ;; break the loop, if number is 0 (br_if $again (get_local $number))) ;; digit chars are now in the reverse order - swap them in a loop (set_local $adr2 (i32.sub (get_local $dest) (i32.const 1))) (loop $again ;; prepare address for later store operation (get_local $adr2) ;; load value on the left (i32.load8_u offset=0x0 align=1 (get_local $adr1)) ;; prepare address for later store operation (get_local $adr1) ;; load value on the right (i32.load8_u offset=0x0 align=1 (get_local $adr2)) ;; write value on the left (i32.store8 offset=0x0 align=1) ;; write value on the right (i32.store8 offset=0x0 align=1) ;; increase $adr1 (set_local $adr1 (i32.add (get_local $adr1) (i32.const 1))) ;; decrease $adr2 (set_local $adr2 (i32.sub (get_local $adr2) (i32.const 1))) ;; loop until adr1 and adr2 cross/meet (br_if $again (i32.lt_u (get_local $adr1) (get_local $adr2)))) ;; return the address we finished at (get_local $dest)) (func $add (param $number1 i32) (param $number2 i32) (result i32) (i32.add (get_local $number1) (get_local $number2))) (func $main (local $counter i32) (local $adr i32) ;; reset the timer (although it should already be resetted, anyway...) (i32.store16 offset=0x1BFC08 align=2 (i32.const 0x0) (i32.const 0x0)) ;; initialize counter (set_local $counter (i32.const 0)) ;; add numbers from 1 to 100 (i32.const 0) (loop $again (param i32) (result i32) (set_local $counter (call $add (get_local $counter) (i32.const 1))) (i32.add (get_local $counter)) (br_if $again (i32.lt_u (get_local $counter) (i32.const 100)))) ;; we now leave sum on the stack for later ;; light led2 (i32.store16 offset=0x1BFC06 align=2 (i32.const 0x0) (i32.const 0x1)) ;; print timer value (set_local $adr (call $print ;; read timer value (i32.load16_u offset=0x1BFC08 align=2 (i32.const 0x0)) (i32.const 0xFFC00))) ;; print ascii underscore '_' (i32.store8 offset=0x0 align=1 (get_local $adr) (i32.const 0x5F)) ;; print computed sum (set_local $adr (call $print ;; sum's already on the stack (i32.add (get_local $adr) (i32.const 1)))) ;; write a non-zero value to the VGA power-on reg at 0x100600 (0x100A00) (i32.store16 offset=0x100600 align=2 (i32.const 0) (i32.const 1))) (export "main" (func $main)))