aboutsummaryrefslogtreecommitdiff
;; 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 $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 (i32.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)))