Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- JSR program ; kernel code/data begins below
- kern_version:
- DAT 0x0017 ; Current version is 1.7.
- ; Changelog:
- ; 1.7 - Currently, this is a beta-test version, with experimental floating-point support. Also, fixed sub-second clock support AGAIN.
- ; 1.6 - Slight optimization in disp_cls.
- ; 1.5 - Fixed sub-second clock support
- ; 1.4 = ???
- ; 1.3 = ???
- ; 1.2 = ???
- ; 1.1 = Added RNG
- ; TODO:
- ; o Finish the Math/Floating Point API
- ; o Find a way to test the Floating Point API
- kern_interrupt_table:
- DAT 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 ; 32 words
- DAT 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 ; 32 words
- DAT 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 ; 32 words
- DAT 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 ; 32 words
- ; 128 words have been allocated to the interrupt table.
- ; To register an interrupt handler, set one of the words in the table to the address of your interrupt handler.
- ; Then trigger the interrupt with the offset as the message.
- ;
- ; For example, to register an interrupt for message 5, set [kern_interrupt_table+5] to the address of the interrupt handler.
- ; Custom interrupt handlers should NOT return with RFI. Instead, they should simply return with "SET PC, POP", as you would return from a normal subroutine.
- ; In addition, you should NOT change the value of IAS at any point during execution; that will break clock ticks and consequently the system clock/timer.
- ;
- hw_keyboard:
- DAT 0xFAC7
- hw_monitor:
- DAT 0xFAC7
- hw_vectordisp:
- DAT 0xFAC7
- hw_clock:
- DAT 0xFAC7
- hw_floppydrive:
- DAT 0xFAC7
- DAT 0xDA7A
- sys_clock_humantime: ; Holds # of seconds since system startup
- DAT 0, 0, 0, 0, 0 ; 80 bits for time_t. (The 5th word is for the sub-second part)
- DAT 0xDA7A
- sys_clock_tick:
- DAT 0, 0, 0, 0 ; 64 bits for a tick counter
- DAT 0xDA7A
- sys_timer: ; countdown timer
- DAT 0
- DAT 0xDA7A
- kern_rng_state: ; RNG internal state
- DAT 0
- kern_code_start: ; code start marker, to help with addressing
- ; -----------------------------------------------------------------------
- ; hw_detect - detect and register system peripherals
- ; Detects all connected hardware and registers the first found peripherals to the various hw_XXXX data locations.
- ; This subroutine should be called before anything else; almost all of the other functions depend on the hw data locations in some way.
- hw_detect:
- SET PUSH, A
- SET PUSH, B
- SET PUSH, C
- SET PUSH, X
- SET PUSH, Y
- SET PUSH, I
- SET PUSH, J
- HWN I
- find_hw_loop:
- IFE I, 0xFFFF
- SET PC, find_hw_return
- HWQ I
- IFE A, 0xB402 ;
- JSR find_hw_clock_subcond
- IFE A, 0x7406
- JSR find_hw_keyboard_subcond
- IFE A, 0x24C5
- JSR find_hw_floppydrive_subcond
- IFE A, 0xF615
- JSR find_hw_monitor_subcond
- IFE A, 0xBF3C
- JSR find_hw_vectordisp_subcond
- STD PC, find_hw_loop
- find_hw_initkeyboard:
- IFN [hw_keyboard], 0xFAC7
- SET PC, POP
- SET [hw_keyboard], I
- SET PC, POP
- find_hw_initclock:
- IFN [hw_clock], 0xFAC7
- SET PC, POP
- SET [hw_clock], I
- SET B, 6 ; 1 "tick": 1/10th of a second
- SET A, 0
- HWI I
- IAS kern_interrupt_handler
- SET B, 0xC10C
- SET A, 2
- HWI I
- SET PC, POP
- find_hw_initfloppydrive:
- IFN [hw_floppydrive], 0xFAC7
- SET PC, POP
- SET [hw_floppydrive], I
- SET PC, POP
- find_hw_initvectordisp:
- IFN [hw_vectordisp], 0xFAC7
- SET PC, POP
- SET [hw_vectordisp], I
- SET PC, POP
- find_hw_initmonitor:
- IFN [hw_monitor], 0xFAC7
- SET PC, POP
- SET [hw_monitor], I
- SET A, 0
- SET B, 0x8000
- HWI I
- SET PC, POP
- find_hw_clock_subcond:
- IFE B, 0x12D0
- JSR find_hw_initclock
- SET PC, POP
- find_hw_keyboard_subcond:
- IFE B, 0x30CF
- JSR find_hw_initkeyboard
- SET PC, POP
- find_hw_floppydrive_subcond:
- IFE B, 0x4FD5
- JSR find_hw_initfloppydrive
- SET PC, POP
- find_hw_monitor_subcond:
- IFE B, 0x7349
- JSR find_hw_initmonitor
- SET PC, POP
- find_hw_vectordisp_subcond:
- IFE B, 0x42BA
- JSR find_hw_initvectordisp
- SET PC, POP
- find_hw_return:
- SET J, POP
- SET I, POP
- SET Y, POP
- SET X, POP
- SET C, POP
- SET B, POP
- SET A, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; kern_interrupt_handler - Kernel interrupt handler
- ; This function handles clock ticks and any other interrupts.
- kern_interrupt_handler:
- IFE A, 0xC10C
- JSR clock_tick
- IFL A, 128
- JSR [A+kern_interrupt_table] ; jump to custom interrupt handlers
- RFI 0xC0DE
- ; -----------------------------------------------------------------------
- ; kern_sleep - pause execution
- ; This function waits for a variable amount of time, which is stored in register X.
- ; The unit of time is deciseconds (i.e setting X to 50 and calling this waits for 5 seconds.)
- ; The end-programmer could probably do this on his own, we're just putting it here as a "test".
- kern_sleep:
- SET [sys_timer], X
- kern_sleep_loop:
- IFG [sys_timer], 0
- SET PC, kern_sleep_loop
- SET PC, POP
- ; Floating point numbers are assumed to be in the format:
- ; (word 1): [significant digits] (word 2): [exponent]
- ; Both words are assumed to be signed. If the significant digits are negative, then the whole floating point number is assumed to be negative as well.
- ; A floating point number is equivalent to this expression:
- ; [significant] * [base]^[exponent].
- ; So, .a floating-point number:
- ; (w1): [0x0005] (5) (w2): [0xFFF3] (-4) = 5 * 2^-4, or 0.3125
- ; In this documentation, a floating point number may be called a "float".
- ; -----------------------------------------------------------------------
- ; floating_greater_than - greater_than for floating point numbers
- ; This function sets C to 1 if the float pointed to by A is greater than the one pointed by B, and 0 otherwise.
- floating_greater_than:
- SET PUSH, X
- SET PUSH, Y
- SET X, [A]
- SET Y, [B]
- AND X, 0x0001
- AND Y, 0x0001
- XOR X, Y
- IFG X, 0
- SET PC, floating_greater_than_signs_differ
- IFA [A+1], [B+1]
- SET PC, floating_greater_than_true
- IFU [A+1], [B+1]
- SET PC, floating_greater_than_false
- IFA [A], [B]
- SET PC, floating_greater_than_true
- IFU [A], [B]
- SET PC, floating_greater_than_false
- SET PC, floating_greater_than_false
- floating_greater_than_true:
- SET C, 1
- SET PC, floating_greater_than_ret
- floating_greater_than_false:
- SET C, 0
- floating_greater_than_ret:
- SET Y, POP
- SET X, POP
- SET PC, POP
- floating_greater_than_signs_differ:
- IFE X, 1 ; If A is positive, then B is negative.
- SET PC, floating_greater_than_true
- SET PC, floating_greater_than_false ; If A is negative, then B is positive.
- ; -----------------------------------------------------------------------
- ; floating_less_than - less_than for floating point numbers
- ; This function sets C to 1 if the float pointed to by A is less than the one pointed by B, and 0 otherwise.
- floating_less_than:
- SET PUSH, X
- SET PUSH, Y
- SET X, [A]
- SET Y, [B]
- AND X, 0x0001
- AND Y, 0x0001
- XOR X, Y
- IFG X, 0
- SET PC, floating_less_than_signs_differ
- IFU [A+1], [B+1]
- SET PC, floating_less_than_true
- IFA [A+1], [B+1]
- SET PC, floating_less_than_false
- IFU [A], [B]
- SET PC, floating_less_than_true
- IFA [A], [B]
- SET PC, floating_less_than_false
- SET PC, floating_less_than_false
- floating_less_than_true:
- SET C, 1
- SET PC, floating_less_than_ret
- floating_less_than_false:
- SET C, 0
- floating_less_than_ret:
- SET Y, POP
- SET X, POP
- SET PC, POP
- floating_less_than_signs_differ:
- IFE X, 1
- SET PC, floating_less_than_false
- SET PC, floating_less_than_true
- ; -----------------------------------------------------------------------
- ; floating_shift - Shift the exponents to prepare for operations
- ; This aligns the exponents in the registers.
- ; Float A is assumed to be in registers X (exponent) and Y (mantissa).
- ; Float B is assumed to be in registers I (exponent) and J (mantissa).
- floating_shift:
- SET PUSH, Z
- IFG X, I ; We need to shift A's mantissa (which is in Y)
- JSR floating_shift_exponent_A
- IFL X, I ; We need to shift B's mantissa (which is in J)
- JSR floating_shift_exponent_B
- SET Z, POP
- SET PC, POP
- floating_shift_exponent_A:
- SET Z, X
- SUB Z, I
- SHL Y, Z
- SET PC, POP
- floating_shift_exponent_B:
- SET Z, I
- SUB Z, X
- SHL J, Z
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; floating_add - addition for floating point numbers
- ; This sets [A] to [A]+[B].
- ; 1 Stack Overflow question and 4 lecture notes later...
- ; If you shift the mantissa (fractional part) to the LEFT, you DECREASE the exponent
- ; If you shift it to the RIGHT, you increase it. Someone needs to double-check me on this...
- ; We want the bits to fall off the least significant end of the mantissa, se we need to shift LEFT.
- floating_add:
- SET PUSH, X ; A-exponent
- SET PUSH, Y ; A-mantissa
- SET PUSH, I ; B-exponent
- SET PUSH, J ; B-mantissa
- SET PUSH, Z ; temp. storage
- SET PUSH, C ; temp. storage
- SET X, [A+1]
- SET Y, [A]
- SET I, [B+1]
- SET J, [B]
- ; Align the exponents (i.e make them the same)
- JSR floating_shift
- ; Add
- SET C, Y ; Add the mantissas
- ADD C, J
- ; Return
- SET [A], C
- SET [A+1], X
- SET C, POP
- SET J, POP
- SET I, POP
- SET Y, POP
- SET X, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; floating_sub - Subtraction for floating point numbers
- ; This function sets [A] to [A]+[B].
- floating_sub:
- SET PUSH, X ; A-exponent
- SET PUSH, Y ; A-mantissa
- SET PUSH, I ; B-exponent
- SET PUSH, J ; B-mantissa
- SET PUSH, C ; temp. storage
- SET X, [A+1]
- SET Y, [A]
- SET I, [B+1]
- SET J, [B]
- ; Align the exponents (i.e make them the same)
- JSR floating_shift
- ; Subtract
- IFG Y, J
- SET PC, floating_sub_A
- SET PC, floating_sub_B
- floating_sub_A:
- SET C, Y
- SUB C, J
- SET PC, floating_sub_ret
- floating_sub_B:
- SET C, J ; Subtract the mantissas
- SUB C, Y
- ; Return
- floating_sub_ret:
- SET [A], C
- SET [A+1], X ; just borrow A's exponent
- SET C, POP
- SET J, POP
- SET I, POP
- SET Y, POP
- SET X, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; floating_mul - Multiplication for floats
- ; This function sets [A] to [A]*[B]
- floating_mul:
- SET PUSH, X ; A-exponent
- SET PUSH, Y ; A-mantissa
- SET PUSH, I ; B-exponent
- SET PUSH, J ; B-mantissa
- SET PUSH, C ; temp. storage
- SET PUSH, Z ; temp. storage
- SET X, [A+1]
- SET Y, [A]
- SET I, [B+1]
- SET J, [B]
- ; Multiply the mantissas:
- SET C, Y
- MLI C, J ; use a signed operator
- ; Add the exponents
- SET Z, X
- ADD Z, I
- ; Return
- SET [A], C
- SET [A+1], Z ; Use the new exponent
- SET Z, POP
- SET C, POP
- SET J, POP
- SET I, POP
- SET Y, POP
- SET X, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; floating_div - Division for floats
- ; This function sets [A] to [A]/[B]
- floating_div:
- SET PUSH, X ; A-exponent
- SET PUSH, Y ; A-mantissa
- SET PUSH, I ; B-exponent
- SET PUSH, J ; B-mantissa
- SET PUSH, C ; temp. storage
- SET PUSH, Z ; temp. storage
- SET X, [A+1]
- SET Y, [A]
- SET I, [B+1]
- SET J, [B]
- ; Divide the mantissas:
- SET C, Y
- DIV C, J ; use a signed operator
- ; Subtract the exponents
- SET Z, X
- SUB Z, I
- ; Return
- SET [A], C
- SET [A+1], Z ; Use the new exponent
- SET Z, POP
- SET C, POP
- SET J, POP
- SET I, POP
- SET Y, POP
- SET X, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; math_abs - Return absolute value
- ; This sets register B to the absolute value of the number contained in register B.
- math_abs:
- IFA B, 0 ; Is B positive?
- SET PC, POP ; If so, then return.
- XOR B, 0xFFFF ; XOR by 0xFFFF is the same as NOT.
- ADD B, 1 ; The number's in one's complement, so add 1 to fix that.
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; math_flipsign - Flip a number's sign ( 5 -> -5 )
- ; This flips the sign of the number in register B.
- math_flipsign:
- XOR B, 0xFFFF
- ADD B, 1
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; math_exponent - Calculate an exponent
- ; This calculates A to the power of B, where A and B refer to the registers of the DCPU-16.
- ; The result is stored in C.
- ;
- math_exponent:
- SET PUSH, I
- SET PUSH, A
- SET I, B
- exp_loop:
- IFL I, 1
- SET PC, exp_end
- MUL A, B
- STD PC, exp_loop
- exp_end:
- SET C, A
- SET A, POP
- SET I, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; clock_tick - Clock tick handler (human seconds / system ticks)
- ; This function increments sys_clock_humantime and sys_clock_tick by 1.
- ; One "tick" is 1/10th of a second.
- clock_tick:
- SET PUSH, A
- SET A, sys_clock_tick
- ADD [A], 1
- ADD [A+1], EX
- ADD [A+2], EX
- ADD [A+3], EX
- SET A, sys_clock_humantime
- ADD [A], 1
- IFG [sys_timer], 0
- SUB [sys_timer], 1
- IFL [A], 10
- SET PC, clock_tick_skip
- ; we should not be here unless a second has passed
- SET [A], 0
- ADD [A+1], 1
- ADD [A+2], EX
- ADD [A+3], EX
- ADD [A+4], EX
- clock_tick_skip:
- SET A, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; disp_write_str - write a null-terminated string to screen.
- ; Writes data to the screen at the coordinates stored in registers X and Y, starting at Z.
- ; Formatting can be set with register C; the data is ORed with the value in C before being written to memory.
- ;
- ; Dev. Notes:
- ; takes (X,Y) coords in X and Y, a pointer to data in Z, and a formatting byte in C
- disp_write_str:
- SET PUSH, X
- SET PUSH, Y
- SET PUSH, Z
- write_str_to_screen_loop:
- SET PUSH, I
- SET I, Y
- MUL I, 32
- ADD I, X
- ADD I, 0x8000
- SET PUSH, J
- SET J, [Z]
- BOR J, C
- SET [I], J
- SET J, POP
- SET I, POP
- ADD Z, 1
- IFE [Z], 0
- SET PC, write_str_to_screen_loopdone
- ADD X, 1
- SET PUSH, I
- SET I, X
- DIV I, 32
- ADD Y, I
- SET I, POP
- MOD X, 32
- SET PC, write_str_to_screen_loop
- write_str_to_screen_loopdone:
- SET Z, POP
- SET Y, POP
- SET X, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; int_to_str - Hex to ASCII.
- ; Sets B to the value for a string given a half-byte in A. (A must be 0x0 - 0xF.). All values are in ASCII.
- ; There's probably a better way to do this, but I feel that this is "safer" (aka I know it works)
- int_to_str:
- IFG A, 0xF ; do some basic checking to see if we're in range (I do it now, instead of later, to save time.)
- SET PC, POP ; On a completely unrelated note, why do I keep switching between "I" and "we"? Well, I guess we'll never know.
- IFU A, 0
- SET PC, POP
- IFE A, 0
- SET B, 0x0030
- IFE A, 1
- SET B, 0x0031
- IFE A, 2
- SET B, 0x0032
- IFE A, 3
- SET B, 0x0033
- IFE A, 4
- SET B, 0x0034
- IFE A, 5
- SET B, 0x0035
- IFE A, 6
- SET B, 0x0036
- IFE A, 7
- SET B, 0x0037
- IFE A, 8
- SET B, 0x0038
- IFE A, 9
- SET B, 0x0039
- IFE A, 0xA
- SET B, 0x0041
- IFE A, 0xB
- SET B, 0x0042
- IFE A, 0xC
- SET B, 0x0043
- IFE A, 0xD
- SET B, 0x0044
- IFE A, 0xE
- SET B, 0x0045
- IFE A, 0xF
- SET B, 0x0046
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; disp_write_word - Write a hex word to the screen.
- ; Writes a single hex word (e.g 0xFACE) to the screen without a leading "0x".
- ; The word will start at the coordinates stored in registers X and Y (like disp_write_str).
- ; The word to be written is read from register Z, and formatting is done with register C, similar to disp_write_str.
- ;
- ; Dev. Notes:
- ; target loc in in X,Y (same as write_str), word is in Z
- disp_write_word:
- ; We can use A/B/C/I/J registers
- SET PUSH, A
- SET PUSH, B
- SET PUSH, C
- SET PUSH, I
- SET PUSH, J
- ; split I into 4 hex digits
- SET A, Z
- SET B, Z
- SET C, Z
- SET I, Z
- SHR C, 4
- SHR B, 8
- SHR A, 12
- AND A, 0x000F
- AND B, 0x000F
- AND C, 0x000F
- AND I, 0x000F
- SET J, write_word_tempdata
- SET PUSH, B
- JSR int_to_str
- SET [J], B
- SET B, POP
- SET PUSH, B
- SET PUSH, A
- SET A, B
- JSR int_to_str
- SET [J+1], B
- SET A, POP
- SET B, POP
- SET PUSH, B
- SET PUSH, A
- SET A, C
- JSR int_to_str
- SET [J+2], B
- SET A, POP
- SET B, POP
- SET PUSH, B
- SET PUSH, A
- SET A, I
- JSR int_to_str
- SET [J+3], B
- SET A, POP
- SET B, POP
- SET J, POP
- SET I, POP
- SET C, POP
- SET B, POP
- SET A, POP
- SET PUSH, Z
- SET Z, write_word_tempdata
- JSR disp_write_str
- SET Z, POP
- SET PC, POP
- write_word_tempdata:
- DAT 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
- ; -----------------------------------------------------------------------
- ; disp_write_float - write a float to screen (as a decimal)
- ; This function writes a floating-point number to screen as a decimal.
- ; As with the other functions, an (X,Y) coordinate is taken as a parameter in registers X and Y. The pointer to the float is taken as Z, and the formatting byte is taken as C.
- ; As of 1.7, this code is nonfunctional. I'll try to get it working soon.
- ; disp_write_float:
- ; SET PUSH, I
- ; JSR disp_x_y_to_i
- ; ; Insert drawing code here
- ; disp_write_float_showneg:
- ; SET [I], 0x002D
- ; BOR [I], C
- ; JSR disp_write_float_incpointer
- ; SET PC, POP
- ; disp_write_float_incpointer:
- ; ADD X, 1
- ; SET PUSH, I
- ; SET I, X
- ; DIV I, 32
- ; ADD Y, I
- ; SET I, POP
- ; MOD X, 32
- ; ADD I, 1
- ; SET PC, POP
- ; disp_write_float_ret:
- ; SET I, POP
- ; SET PC, POP
- ; -----------------------------------------------------------------------
- ; disp_x_y_to_i - Convert (X,Y) coordinates to an array offset
- ; Converts a pair of (X, Y) coordinates stored in X and Y to an array offset stored in I.
- ; (0,0) converts to 0x8000, the address of the first character of the screen (the upper-left).
- disp_x_y_to_i:
- SET I, Y
- MUL I, 32
- ADD I, X
- ADD I, 0x8000
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; disp_cls - Clear the screen.
- ; This function clears all data between 0x8000 and 0x8182.
- ; Thank you to leonardluen (from the 0x10c forums) for some optimization tips that are in use here. To be specific, he made this run 3 cycles faster per loop.
- disp_cls:
- SET PUSH, I
- SET PUSH, A
- SET PUSH, Z
- SET I, 0x8000 ; The LEM1802's area of memory starts at 0x8000 and spans 0x182 words
- SET A, 0x8182
- SET Z, disp_cls_loop
- disp_cls_loop:
- SET [I], 0
- IFE I, A
- STI PC, Z
- SET Z, POP
- SET A, POP
- SET I, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; ascii_applyshift - simulate pressing Shift and typing
- ; This function sets C to the ASCII value it would have if the user had pressed shift.
- ; For example, if C had the value "a" then it would then hold the value "A", and "1" would be changed to "!", and so on.
- ; This only applys to the printable characters.
- ; I need this function because keyboard_read_char doesn't account for shift... (well, it only takes shift into account for the letters.)
- ;
- ascii_applyshift:
- IFE C, 0x31
- SET C, 0x21
- IFE C, 0x32
- SET C, 0x40
- IFE C, 0x33
- SET C, 0x23
- IFE C, 0x34
- SET C, 0x24
- IFE C, 0x35
- SET C, 0x25
- IFE C, 0x36
- SET C, 0x5E
- IFE C, 0x37
- SET C, 0x26
- IFE C, 0x38
- SET C, 0x2A
- IFE C, 0x39
- SET C, 0x28
- IFE C, 0x30
- SET C, 0x29
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; ascii_tocaret - Get a control character from a letter
- ; This converts from a letter to a control character based on caret notation.
- ; The character to be "converted" is read from, and stored in, C.
- ; With caret notation, the decimal code for a character is displayed based with the corresponding letter and a caret (^).
- ; For example, the ASCII code for the Start of Header (SOH) character is 1 in decimal (it's also 1 in hex), so we represent it with ^A.
- ; Likewise, the ASCII code for the Bell (BEL) character is 7 in decimal, so we represent it with ^G.
- ; Since our alphabet has no character 0, we represent the null character (NUL) with ^@. (The ASCII character code for @ comes before the code for A.)
- ; However, there is no way to actually type Ctrl-@ on your keyboard, so you cannot type the null character.
- ;
- ascii_tocaret:
- IFG C, 0x60
- SET PC, ascii_tocaret_subcond_2
- IFG C, 0x40
- SET PC, ascii_tocaret_subcond_1
- ascii_tocaret_subcond_1: ; uppercase characters
- IFL C, 0x5B
- SUB C, 0x40
- SET PC, ascii_tocaret_end
- ascii_tocaret_subcond_2: ; lowercase characters
- IFL C, 0x7B
- SUB C, 0x60
- ascii_tocaret_end:
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; keyboard_read_char - read an ASCII value from the keyboard
- ; This function sets C to an ASCII value based on what character was pressed on the keyboard.
- ; This function DOES NOT ECHO CHARACTERS TO OUTPUT.
- ; "Character keys" are:
- ; 0x20 - 0x7F - ASCII characters (a, z, A, Z, 1, 2, 3, etc.) (http://en.wikipedia.org/wiki/ASCII)
- ; 0x11 - RETURN key, returns ASCII newline ("\n")
- ; 0x10 - BACKSPACE key, returns 0x10.
- keyboard_read_char:
- SET PUSH, A
- keyboard_read_char_loop:
- SET A, 1
- HWI [hw_keyboard]
- IFE C, 0x11
- SET PC, read_char_loop_end
- IFE C, 0x10
- SET PC, read_char_loop_end
- IFE C, 0
- SET PC, keyboard_read_char_loop
- IFG C, 0x7F
- SET PC, keyboard_read_char_loop
- IFL C, 0x20
- SET PC, keyboard_read_char_loop
- read_char_loop_end:
- IFE C, 0x11
- SET C, 0x0A ; 0x0A == '\n'
- SET PUSH, C
- SET A, 2
- SET B, 0x90
- HWI [hw_keyboard]
- SET PUSH, X
- SET X, C
- SET C, [SP+1]
- IFE X, 1
- JSR ascii_applyshift
- SET [SP+1], C
- SET X, POP
- SET C, POP
- SET A, 0
- HWI [hw_keyboard]
- SET A, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; keyboard_read_special - read an special value from the keyboard
- ; This function sets C to the keycode for a special key, based on the one pressed.
- ; Special keys are:
- ; 0x12 - Insert
- ; 0x13 - Delete
- ; 0x80 - Arrow UP
- ; 0x81 - Arrow DOWN
- ; 0x82 - Arrow LEFT
- ; 0x83 - Arrow RIGHT
- ; 0x90 - Shift
- ; 0x91 - Control
- ; The RETURN key (0x11) and the BACKSPACE key are ommitted, as keyboard_read_char handles those.
- keyboard_read_special:
- SET PUSH, A
- SET A, 1
- keyboard_read_special_loop:
- HWI [hw_keyboard]
- IFE C, 0
- SET PC, keyboard_read_special_loop
- IFE C, 0x11
- SET PC, keyboard_read_special_loop
- IFE C, 0x10
- SET PC, keyboard_read_special_loop
- IFL C, 0x20
- SET PC, keyboard_read_special_loop_end
- IFG C, 0x7F
- SET PC, keyboard_read_special_loop_end
- keyboard_read_special_loop_end:
- SET A, 0
- HWI [hw_keyboard]
- SET A, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; keyboard_read_raw - read a key from the keyboard
- ; This function sets C to the keycode for any key.
- ; This function DOES NOT ECHO CHARACTERS TO OUTPUT.
- keyboard_read_raw:
- SET PUSH, A
- SET A, 1
- keyboard_read_raw_loop:
- HWI [hw_keyboard]
- IFE C, 0
- SET PC, keyboard_read_raw_loop
- SET A, 0
- HWI [hw_keyboard]
- SET A, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; keyboard_read_str - read a null-terminated string to an address in memory
- ; This function writes all characters pressed to Z, echoing each character written.
- ; This function echoes characters to the memory address pointed to by I, which should be set to 0x8000 - 0x8182.
- ; If I is zero, then characters are not echoed to the terminal.
- ; This is, in essence, C's getline or C++'s cin.
- keyboard_read_str:
- SET PUSH, C
- SET PUSH, Z
- SET PUSH, I
- keyboard_read_str_loop:
- JSR keyboard_read_char
- IFE C, 0x0A
- SET PC, keyboard_read_str_return
- IFE C, 0x10
- SET PC, keyboard_read_str_backspace
- SET [Z], C
- SET [I], C ; actually draw the character
- BOR [I], 0xF000 ; Set [I] to white-on-black
- IFN [Z+2], 0 ; Would we overwrite data during the next iteration? (Z+1 is the next character, but we offset by 2 to take the null terminator into account.)
- SET PC, keyboard_read_str_return ; Z+1 would be set to null.
- ADD Z, 1
- ADD I, 1
- SET PC, keyboard_read_str_loop
- keyboard_read_str_backspace:
- SUB I, 1
- SUB Z, 1
- SET [I], 0
- SET [Z], 0
- IFG [SP+1], Z ; If Old-Z is greater than new-z...
- SET Z, [SP+1]
- IFG [SP], I
- SET I, [SP]
- SET PC, keyboard_read_str_loop
- keyboard_read_str_return:
- SET [Z+1], 0 ; null-terminate the string
- SET I, POP
- SET Z, POP
- SET C, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; floppy_drive_block_read - read a sector to memory from a floppy (blocking)
- ; This sector will attempt to read the sector in register X from a floppy to the area of memory pointed to by register Y.
- ; This is effectively a wrapper around the interrupts required to read a sector from the disk, and blocks until it is finished.
- floppy_drive_block_read:
- SET PUSH, A
- SET PUSH, B
- SET PUSH, C
- SET A, 0
- HWI [hw_floppydrive]
- IFE B, 1
- SET PC, floppy_drive_block_read_logic
- IFE B, 2
- SET PC, floppy_drive_block_read_logic
- floppy_drive_block_read_return:
- SET C, POP
- SET B, POP
- SET A, POP
- SET PC, POP
- floppy_drive_block_read_logic:
- SET A, 2
- HWI [hw_floppydrive]
- IFN B, 1
- SET PC, floppy_drive_block_read_return
- SET A, 0
- HWI [hw_floppydrive]
- IFE B, 3
- SUB PC, 3
- SET PC, floppy_drive_block_read_return
- ; -----------------------------------------------------------------------
- ; floppy_drive_block_write - write to a sector on a floppy from memory
- ; This sector will attempt to write to the sector in register X on a floppy from the area of memory pointed to by register Y.
- ; This is effectively a wrapper around the interrupts required to wrote a sector to the disk, and blocks until it is finished.
- floppy_drive_block_write:
- SET PUSH, A
- SET PUSH, B
- SET PUSH, C
- SET A, 0
- HWI [hw_floppydrive]
- IFE B, 1
- SET PC, floppy_drive_block_write_logic
- floppy_drive_block_write_return:
- SET C, POP
- SET B, POP
- SET A, POP
- SET PC, POP
- floppy_drive_block_write_logic:
- SET A, 3
- HWI [hw_floppydrive]
- IFN B, 1
- SET PC, floppy_drive_block_write_return
- SET A, 0
- HWI [hw_floppydrive]
- IFE B, 3
- SUB PC, 3
- SET PC, floppy_drive_block_write_return
- ; -----------------------------------------------------------------------
- ; ecc_generate_parity - Generate a (simple) checksum for an arbitrary amount of data
- ; This function generates an error-checking code (to be precise, a parity word) for a block of data.
- ; The block is read starting from register I and continues for J bytes.
- ; The ECC is stored in register B.
- ; This function generates an error-CHECKING code, and is one way (i.e cannot be used to get the data back).
- ; To check, simply run this function again with the same parameters. If the result is the same, nothing's changed.
- ecc_generate_parity:
- SET PUSH, I
- SET PUSH, J
- ADD J, I
- SET B, [I]
- ecc_generate_parity_loop:
- IFE I, J
- SET PC, ecc_generate_parity_loop_end
- ADD I, 1
- XOR B, [I]
- ecc_generate_parity_loop_end:
- SET J, POP
- SET I, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; ecc_generate_crc - Generate a crc for a block of data.
- ; This function calculates an 8-bit CRC for a block of data pointed to by I with a length of J.
- ; This function returns the CRC in B.
- ; The generator polynomial is 0x8408 (CRC-16-CCITT).
- ecc_generate_crc:
- ; The bitmask for the leftmost divisor bit is 8000 (1000000000000000 in binary)
- ; input is in I/J
- ; result goes to B
- ; loop2 counter goes to Y.
- ; X is used as a bitmask in the inner loop, and as an "end-word" register (aka it contains the last word to be CRC'd)
- SET PUSH, A
- SET PUSH, C
- SET PUSH, X
- SET PUSH, Y
- SET C, 0
- SET X, I
- ADD X, J
- SET B, 0
- ecc_gen_crc_loop:
- IFE I, X
- SET PC, ecc_gen_crc_loop_end
- XOR B, [I]
- SET Y, 0
- ecc_gen_crc_loop_2:
- IFE Y, 8
- SET PC, ecc_gen_crc_loop_2_end
- SET PUSH, X
- SET X, B
- AND X, 0x0001
- SHR B, 1
- IFG X, 0
- XOR B, 0x8408
- ADD Y, 1
- SET X, POP
- SET PC, ecc_gen_crc_loop_2
- ecc_gen_crc_loop_2_end:
- ADD I, 1
- ADD C, 1
- SET PC, ecc_gen_crc_loop
- ecc_gen_crc_loop_end:
- SET Y, POP
- SET X, POP
- SET C, POP
- SET A, POP
- SET PC, POP
- ; -----------------------------------------------------------------------
- ; rand_gen_lfsr - generate a pseudorandom number with a Linear Feedback Shift Register
- ; This function sets B to a pseudorandom number generated with a LFSR (polynomial 004B, or x^16+x^14+x^13+x^11+1).
- ; To seed the RNG, set [kern_rng_state] to any value.
- rand_gen_lfsr:
- SET B, [kern_rng_state] ; B will store the output bit (and later, the resulting state)
- AND B, 0x8000 ; Get LSB (output bit)
- SHL [kern_rng_state], 1 ; Shift register
- BOR [kern_rng_state], EX ; Make sure we get the excess
- IFG B, 0 ; Is the output bit on?
- XOR [kern_rng_state], 0x004B ; If so, then XOR the state with the tap-value, or 0x004B.
- SET B, [kern_rng_state] ; Set B to the output
- SET PC, POP ; return
- kern_code_end:
- DAT 0xC0DE ; kernel end codeword
- program:
- ;
- ; This program simply calculates a parity word for the (assembled) kernel and displays it to the user. This tests disp_write_str, disp_write_word, and ecc_generate_parity.
- ; This program simply calculates the CRC of the (assembled) kernel and displays it to the user. This tests the above and ecc_generate_crc.
- ; It then sleeps for a second, testing the clock and sys_timer.
- ; It then prompts the user to type in anything, and echoes the typed string back to the user. This tests keyboard_read_str and disp_x_y_to_i.
- ; It then sleeps for 5 seconds.
- ; Then, it jumps to one of three random loops: "Bitmap", "FuzzyScreen", or "Psychedelic". These write random words (or values derived from such) to the screen. This tests the RNG and its randomness, as well as the clock.
- ; Note that of these three, "Psychedelic" is the best to use for testing, as it is the only one that uses all 16 bits of the random value. The other loops only test the lowest two bits.
- ;
- JSR hw_detect
- SET X, 0
- SET Y, 1
- SET C, 0xF000
- SET Z, program_str1
- JSR disp_write_str
- SET X, 0
- SET Y, 0
- SET C, 0xF000
- SET Z, program_str2
- JSR disp_write_str
- SET I, kern_code_start
- SET J, kern_code_end
- SUB J, I
- JSR ecc_generate_parity
- SET [program_parity1], B
- SET X, 17
- SET Y, 0
- SET C, 0xA000
- SET Z, [program_parity1]
- JSR disp_write_word
- SET X, 22
- SET Y, 0
- SET C, 0xF000
- SET Z, program_period
- JSR disp_write_str
- SET I, kern_code_start
- SET J, kern_code_end
- SUB J, I
- JSR ecc_generate_crc
- SET [program_crc1], B
- SET X, 14
- SET Y, 1
- SET C, 0xA000
- SET Z, [program_crc1]
- JSR disp_write_word
- SET X, 18
- SET Y, 1
- SET C, 0xF000
- SET Z, program_period
- JSR disp_write_str
- SET X, 0
- SET Y, 2
- SET Z, program_str3
- SET C, 0xF000
- JSR disp_write_str
- SET X, 15
- SET Y, 2
- JSR disp_x_y_to_i
- SET Z, program_dat1
- JSR keyboard_read_str
- SET X, 0
- SET Y, 3
- JSR disp_write_str
- SET X, 19
- SET Y, 0
- SET C, 0xF000
- SET Z, program_period
- JSR disp_write_str
- SET X, 50
- JSR kern_sleep
- SET A, 1
- HWI [hw_clock]
- SET X, C
- ; At this point, we can jump to one of three "random display" loops:
- ; * Bitmap: Set characters on the screen to uniform black or white
- ; * FuzzyScreen: Produce a "static" effect
- ; * Psychedelic: Produce a wide variety of colors/characters
- ;SET PC, rand_fuzzyscreen
- SET PC, rand_psychedelic ; Feel free to change the instructions (they both work, in other words)
- ; Bitmap is the "default"
- rand_bitmap:
- SET I, 0x8000
- rand_bitmap_loop:
- SET A, 1
- HWI [hw_clock]
- IFE C, X ; Has there been a "tick" since we last checked?
- SET [kern_rng_state], C
- IFE C, X
- SET X, C
- IFG I, 0x8182
- SET PC, rand_bitmap
- JSR rand_gen_lfsr
- SET Y, B
- AND Y, 0x8000
- SET A, 0x0020
- IFG Y, 0
- BOR A, 0xF000
- IFE Y, 0
- BOR A, 0x0F00
- SET [I], A
- ADD I, 1
- SET PC, rand_bitmap_loop
- rand_fuzzyscreen:
- SET I, 0x8000
- rand_fuzzyscreen_loop:
- SET A, 1
- HWI [hw_clock]
- IFE C, X ; Has there been a "tick" since we last checked?
- SET [kern_rng_state], C
- IFE C, X
- SET X, C
- IFG I, 0x8182
- SET PC, rand_fuzzyscreen
- JSR rand_gen_lfsr
- SET Y, B
- SET A, B
- AND Y, 0x8000
- AND A, 0x3000
- SHR A, 12
- IFG Y, 0
- BOR A, 0xF000
- IFE Y, 0
- BOR A, 0x0F00
- SET [I], A
- ADD I, 1
- SET PC, rand_fuzzyscreen_loop
- rand_psychedelic:
- SET I, 0x8000
- rand_psychedelic_loop:
- SET A, 1
- HWI [hw_clock]
- IFE C, X ; Has there been a "tick" since we last checked?
- SET [kern_rng_state], C
- IFE C, X
- SET X, C
- IFG I, 0x8182
- SET PC, rand_psychedelic
- JSR rand_gen_lfsr
- SET [I], B
- ADD I, 1
- SET PC, rand_psychedelic_loop
- program_crc1:
- DAT 0
- program_parity1:
- DAT 0
- program_crc2:
- DAT 0
- program_dat1:
- DAT 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 32 words for the readbuffer
- program_period:
- DAT ".", 0
- program_str1:
- DAT "CRC code is 0x", 0 ; length is 14
- program_str2:
- DAT "Parity code is 0x", 0 ; length is 20
- program_str3:
- DAT "Type anything. ", 0 ;length is 15
- program_str4:
- DAT "System clock", 0
- program_str5:
- DAT "Test String!", 0 ; 54 65 73 74 20 53 74 72 69 6E 67
- ; In the notes for this kernel, the terms "function" and "subroutine" are to be interpreted as the same thing, despite the slight differences between the terms.
- ; **********************************************************************************************************************************************************************************************************************************************************************
- ;
- ; -1 Up / KillaVanilla's DCPU-16 Kernel / API
- ; ( also known as UnnamedAPI )
- ; Current Version: 1.4
- ; ----------------------------
- ; Current Features:
- ; * System Clock / Countdown Timer:
- ; The system clock stores the number of seconds since the DCPU-16 booted as a 80-bit little endian unsigned integer. (64 bits for the seconds, 16 bits for the sub-second.)
- ; Each system tick is 1/10th of a second.
- ; The countdown timer decreases by one every tick, and is 1 word in length.
- ; * HW Detection:
- ; This is pretty much a standard feature on almost every kernel out there.
- ; The hw_detect function / subroutine saves hardware IDs to special addresses in memory that can be accessed with labels, such as "hw_vectordisp" (SPED-3 Vector Display) and "hw_keyboard" (Generic Keyboard).
- ; * Multiple Interrupt Handlers:
- ; This kernel supports a "kernel interrupt table" that allows you to set custom interrupt handlers for interrupts 0-127.
- ; * "Register-Safe" Code:
- ; All register values are pushed to the stack before any subroutine begins execution. Therefore, you can rest assured that your registers won't unexpectedly change due to subroutine calls.
- ; * Keyboard API:
- ; Has four functions:
- ; keyboard_read_raw: Blocks until the user presses a key.
- ; keyboard_read_char: Blocks until the user presses one of the ASCII character keys, RETURN, or BACKSPACE (aka the "text input" keys.) (also accounts for Shift).
- ; keyboard_read_special: Blocks until the user presses a "special" key (aka any key that is not a letter, RETURN, or BACKSPACE.)
- ; keyboard_read_str: Writes an entire line of characters to a specified memory address. (Works like C++'s cin and C's getline().)
- ; * Graphics API:
- ; Pretty self-explanatory, and has two functions:
- ; disp_write_str: Writes a null-terminated (C-style) string to the monitor.
- ; disp_write_word: Writes a hex word (like 0xC0DE) to the monitor.
- ; disp_cls: Clears the screen.
- ; * Floppy Drive API:
- ; This isn't much to look at right now. It has two functions:
- ; floppy_drive_block_read: Reads a sector from a floppy disk into the DCPU's RAM, and blocks until the read operation is finished.
- ; floppy_drive_block_read: Writes a sector to a floppy disk from the DCPU's RAM, and blocks until the write operation is finished.
- ; * Error Detection Code API:
- ; Has 2 functions:
- ; ecc_generate_parity: Calculates a parity code for a block of data.
- ; ecc_generate_crc: Calculates a Cyclic Redundancy Check (CRC) code for a block of data.
- ; * Random Number Generator:
- ; The kernel's RNG is implemented as a linear feedback shift register, or LFSR for short. The RNG's state is stored in kern_rng_state.
- ; To seed the RNG, simply set kern_rng_state to any value (clock ticks are a good choice).
- ; I've run one test on this data, and that is to write the data directly to screen. It looks pretty random to me. However, I'm no expert on randomness, so use this RNG at your own risk, and definitely DON'T use this for real-world encryption.
- ; * Floating Point Operations:
- ; The floating point operations included here haven't been tested yet, as I can't find any way to test them. Beware. This API has 7 functions:
- ; floating_add - Add floating point numbers
- ; floating_sub - Subtract floating point numbers
- ; floating_mul - Multiply floating point numbers
- ; floating_div - Divide floating point numbers
- ; floating_shift - Shift the exponents of floating point numbers, so that arithmetic can be done. (It's used internally.)
- ; floating_greater_than - A greater than (>) operator for floating point numbers
- ; floating_less_than - A less than (<) operator for floating point numbers
- ; All functions work with 32 bit floating point numbers.
- ; * Math API:
- ; I'll work on this some more once the Floating Point Operations API is confirmed to work. This currently has 3 functions:
- ; math_abs - Get absolute value
- ; math_flipsign - Flip a number's sign (pos --> neg / neg --> pos)
- ; math_exponent - Calculate an exponent
- ;
- ; Each function has a description before its definition, describing what it does, what parameters it takes (and in what registers), and any other important info.
- ;
- ; There is also a sample program included with the kernel, showing off a few features of the kernel (and is also handy for debugging)
- ;
- ; To be included:
- ; * Vector Display API (Any ideas on functions?)
- ; * FAT FS API (Maybe someday...)
- ;
- ; **********************************************************************************************************************************************************************************************************************************************************************
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement