785 lines
15 KiB
ArmAsm
785 lines
15 KiB
ArmAsm
; Function defined on pp272-273 of C64 Programmers Reference Guide
|
|
chrout:
|
|
;; Crude implementation of character output
|
|
|
|
;; Save X and Y values
|
|
;; (Confirmed by writing a test program that X and Y
|
|
;; don't get modified, in agreement with C64 PRG's
|
|
;; description of CHROUT)
|
|
sta last_printed_character_ascii
|
|
txa
|
|
pha
|
|
tya
|
|
pha
|
|
|
|
jsr hide_cursor_if_visible
|
|
|
|
lda last_printed_character_ascii
|
|
tax
|
|
|
|
;; Check for special characters
|
|
|
|
cmp #$00
|
|
bne not_00
|
|
jmp chrout_done
|
|
not_00:
|
|
|
|
;; Linefeed (simply ignored)
|
|
;; Trivia: BASIC does CRLF with READY. prompt
|
|
cmp #$0a
|
|
bne not_0a
|
|
jmp chrout_done
|
|
not_0a:
|
|
|
|
;; Carriage return
|
|
cmp #$0d
|
|
bne not_0d
|
|
jmp screen_advance_to_next_line
|
|
not_0d:
|
|
|
|
cmp #$94
|
|
bne not_94
|
|
|
|
;; Insert (shift-DELETE)
|
|
|
|
;; Abort if line already max length, ie 79th char is not a space
|
|
jsr get_current_line_logical_length
|
|
cmp #79
|
|
bne definitely_not_too_long
|
|
tay
|
|
lda (current_screen_line_ptr),y
|
|
cmp #$20
|
|
beq definitely_not_too_long
|
|
|
|
;; Line is too long to extend
|
|
jmp chrout_done
|
|
|
|
definitely_not_too_long:
|
|
;; Work out if line needs to be expanded, and if so expand it
|
|
ldy #39
|
|
lda (current_screen_line_ptr),y
|
|
cmp #$20
|
|
beq no_need_to_extend
|
|
|
|
jsr screen_grow_logical_line
|
|
|
|
no_need_to_extend:
|
|
;; Shuffle chars towards end of line
|
|
jsr get_current_line_logical_length
|
|
tay
|
|
*
|
|
;; Note: While the following routine is obvious to any skilled
|
|
;; in the art as the most obvious simple and efficient solution,
|
|
;; if the screen writes are before the colour writes, it results
|
|
;; in a relatively long verbatim stretch of bytes when compared to
|
|
;; the C64 KERNAL. Thus we have swapped the order, just to reduce
|
|
;; the potential for any argument of copyright infringement, even
|
|
;; though we really don't believe that the routine can be copyrighted
|
|
;; due to the lack of creativity.
|
|
dey
|
|
lda (current_screen_line_colour_ptr),y
|
|
iny
|
|
sta (current_screen_line_colour_ptr),y
|
|
dey
|
|
lda (current_screen_line_ptr),y
|
|
iny
|
|
sta (current_screen_line_ptr),y
|
|
|
|
dey
|
|
cpy current_screen_x
|
|
bne -
|
|
;; Put space in the inserted gap
|
|
lda #$20
|
|
sta (current_screen_line_ptr),y
|
|
|
|
;; Increase insert mode count (which causes quote-mode like behaviour)
|
|
inc insert_mode
|
|
|
|
jmp chrout_done
|
|
not_94:
|
|
|
|
;; DELETE in insert mode embeds control character
|
|
ldx insert_mode
|
|
bne not_14
|
|
|
|
cmp #$14
|
|
bne not_14
|
|
|
|
;; Delete
|
|
ldx current_screen_x
|
|
bne delete_non_zero_column
|
|
delete_at_column_0:
|
|
ldy current_screen_y
|
|
bne not_row_0
|
|
;; delete from row 0, col 0 does nothing
|
|
jmp chrout_done
|
|
not_row_0:
|
|
;; Column 0 delete just moves us to the end of the
|
|
;; previous line, without actually deleting anything
|
|
dec current_screen_y
|
|
jsr get_current_line_logical_length
|
|
lda logical_line_length
|
|
|
|
sta current_screen_x
|
|
jsr calculate_screen_line_pointer
|
|
jmp chrout_done
|
|
|
|
delete_non_zero_column:
|
|
;; Copy rest of line down
|
|
jsr get_current_line_logical_length
|
|
ldy current_screen_x
|
|
cpy logical_line_length
|
|
beq done_delete
|
|
dey
|
|
*
|
|
iny
|
|
lda (current_screen_line_ptr),y
|
|
dey
|
|
sta (current_screen_line_ptr),y
|
|
iny
|
|
lda (current_screen_line_colour_ptr),y
|
|
dey
|
|
sta (current_screen_line_colour_ptr),y
|
|
iny
|
|
cpy logical_line_length
|
|
bne -
|
|
|
|
;; Clear char at end of line
|
|
jsr get_current_line_logical_length
|
|
tay
|
|
lda #$20
|
|
sta (current_screen_line_ptr),y
|
|
|
|
done_delete:
|
|
dec current_screen_x
|
|
jmp chrout_done
|
|
not_14:
|
|
|
|
;; Check for quote mode
|
|
ldx quote_mode_flag
|
|
bne is_quote_mode
|
|
ldx insert_mode
|
|
bne is_quote_mode
|
|
jmp not_quote_mode
|
|
|
|
is_quote_mode:
|
|
;; Is it a control code?
|
|
cmp #$20
|
|
bcs not_quote_mode
|
|
|
|
;; Yes, a control code in quote mode means we display it as a reverse character
|
|
and #$1F
|
|
ora #$80
|
|
jmp output_literal_char
|
|
|
|
not_quote_mode:
|
|
|
|
;; Check for colours
|
|
ldx #$f
|
|
colour_check_loop:
|
|
cmp colour_codes,x
|
|
bne +
|
|
stx text_colour
|
|
jmp chrout_done
|
|
* dex
|
|
bpl colour_check_loop
|
|
|
|
;; Compute's Mapping the 64 p38
|
|
cmp #$12
|
|
bne not_12
|
|
lda #$80
|
|
sta reverse_video_flag
|
|
jmp chrout_done
|
|
not_12:
|
|
;; Compute's Mapping the 64 p 38
|
|
cmp #$92
|
|
bne not_92
|
|
lda #$00
|
|
sta reverse_video_flag
|
|
jmp chrout_done
|
|
not_92:
|
|
|
|
;; Check for cursor movement keys
|
|
cmp #$11
|
|
bne not_11
|
|
lda current_screen_x
|
|
clc
|
|
adc #40
|
|
sta current_screen_x
|
|
;; We need to advance the pointer manually, as normalising
|
|
;; now will break things. The pointer update is required
|
|
;; so that we can tell if we really are on the bottom phys
|
|
;; screen line or not
|
|
jsr scroll_up_if_on_last_line
|
|
jsr calculate_screen_line_pointer
|
|
jmp chrout_done
|
|
not_11:
|
|
cmp #$1d
|
|
bne not_1d
|
|
inc current_screen_x
|
|
jsr calculate_screen_line_pointer
|
|
jmp chrout_done
|
|
not_1d:
|
|
cmp #$91
|
|
bne not_91
|
|
lda current_screen_x
|
|
sec
|
|
sbc #40
|
|
sta current_screen_x
|
|
jsr calculate_screen_line_pointer
|
|
jmp chrout_done
|
|
not_91:
|
|
cmp #$9d
|
|
bne not_9d
|
|
dec current_screen_x
|
|
jsr calculate_screen_line_pointer
|
|
jmp chrout_done
|
|
not_9d:
|
|
|
|
;; Home cursor
|
|
cmp #$13
|
|
bne not_13
|
|
lda #0
|
|
sta current_screen_x
|
|
sta current_screen_y
|
|
jsr calculate_screen_line_pointer
|
|
jmp chrout_done
|
|
not_13:
|
|
|
|
;; Clear screen
|
|
cmp #$93
|
|
bne not_clearscreen
|
|
jsr clear_screen
|
|
jmp chrout_done
|
|
|
|
not_clearscreen:
|
|
|
|
;; Convert PETSCII to screen code
|
|
|
|
;; Don't print other control codes
|
|
cmp #$1f
|
|
bcs +
|
|
jmp chrout_done
|
|
*
|
|
|
|
;; Unshifted letters
|
|
cmp #$40
|
|
bcc not_lower
|
|
cmp #$41+26
|
|
bcs not_lower
|
|
and #$1f
|
|
jmp chrout_l1
|
|
|
|
not_lower:
|
|
|
|
;; Shifted letters
|
|
cmp #$60
|
|
bcc not_upper
|
|
cmp #$60+26
|
|
bcs not_upper
|
|
sec
|
|
sbc #$20
|
|
|
|
jmp chrout_l1
|
|
|
|
not_upper:
|
|
cmp #$a0
|
|
bcc not_vendor
|
|
sec
|
|
sbc #$40
|
|
|
|
not_vendor:
|
|
|
|
chrout_l1:
|
|
|
|
output_literal_char:
|
|
|
|
;; Write normal character on the screen
|
|
|
|
pha
|
|
|
|
ldy current_screen_x
|
|
ora reverse_video_flag ; Compute's Mapping the 64 p38
|
|
sta (current_screen_line_ptr),y
|
|
|
|
;; Decrement number of chars waiting to be inserted
|
|
lda insert_mode
|
|
beq +
|
|
dec insert_mode
|
|
*
|
|
pla
|
|
cmp #$22
|
|
bne not_quote
|
|
|
|
;; Toggle quote flag if required
|
|
lda quote_mode_flag
|
|
eor #$80
|
|
sta quote_mode_flag
|
|
|
|
not_quote:
|
|
|
|
;; Set colour
|
|
lda text_colour
|
|
sta (current_screen_line_colour_ptr),y
|
|
|
|
;; Advance the column, and scroll screen down if we need
|
|
;; to insert a 2nd line in this logical line.
|
|
;; (eg Compute's Mapping the 64 p41)
|
|
ldx current_screen_y
|
|
iny
|
|
sty current_screen_x
|
|
cpy #40
|
|
bne +
|
|
jsr screen_grow_logical_line
|
|
ldy current_screen_x
|
|
*
|
|
cpy #80
|
|
bcc no_screen_advance_to_next_line
|
|
lda #0
|
|
sta current_screen_x
|
|
jmp screen_advance_to_next_line
|
|
no_screen_advance_to_next_line:
|
|
|
|
chrout_done:
|
|
jsr show_cursor_if_enabled
|
|
|
|
;; Restore X and Y, set carry to mark success on exit
|
|
pla
|
|
tay
|
|
pla
|
|
tax
|
|
lda last_printed_character_ascii
|
|
clc ; indicate success
|
|
rts
|
|
|
|
screen_grow_logical_line:
|
|
ldy current_screen_y
|
|
;; Don't grow line if it is already grown
|
|
lda screen_line_link_table,y
|
|
bmi done_grow_line
|
|
|
|
lda #$80
|
|
sta screen_line_link_table,y
|
|
|
|
;; Now make space for the extra line added.
|
|
;; If we are on the last physical line of the screen,
|
|
;; Then we need to scroll the screen up
|
|
jsr scroll_up_if_on_last_line
|
|
|
|
;; Scroll screen down to make space
|
|
;; As we are scrolling down, we start from the end,
|
|
;; and work backwards. We can't be as simple and efficient
|
|
;; here as we are for scrolling up, because we don't know
|
|
;; how much must be scrolled.
|
|
;; Simple solution is to work out how many physical lines
|
|
;; need shifting down, and then move lines at a time after
|
|
;; initialising the pointers to the end area of the screen.
|
|
ldx #25-2
|
|
ldy #0
|
|
count_rows_loop:
|
|
dex
|
|
lda screen_line_link_table,y
|
|
bpl +
|
|
dex
|
|
* iny
|
|
cpy current_screen_y
|
|
bne count_rows_loop
|
|
|
|
cpx #0
|
|
beq no_copy_down
|
|
bmi no_copy_down
|
|
|
|
;; Set pointers to end of screen line, and one line
|
|
;; above. (It is always one physical line, because
|
|
;; this can only happen when expanding a line from 40 to
|
|
;; 80 characters).
|
|
;; XXX - This is not very efficient, and longer than it
|
|
;; needs to be. Better would be to work out size to
|
|
;; copy, and count it down in a pointer somewhere, so
|
|
;; that loop iteration logic is simpler.
|
|
lda HIBASE
|
|
clc
|
|
adc #3
|
|
sta current_screen_line_ptr+1
|
|
sta load_or_scroll_temp_pointer+1
|
|
lda #>$DBC0
|
|
sta load_save_verify_end_address+1
|
|
sta current_screen_line_colour_ptr+1
|
|
|
|
lda #<$03C0
|
|
sta current_screen_line_ptr+0
|
|
sta current_screen_line_colour_ptr+0
|
|
;; souce address is line above
|
|
sec
|
|
sbc #40
|
|
sta load_or_scroll_temp_pointer+0
|
|
sta load_save_verify_end_address+0
|
|
|
|
copy_line_down_loop:
|
|
ldy #39
|
|
cl_inner:
|
|
lda (load_save_verify_end_address),y
|
|
sta (current_screen_line_colour_ptr),y
|
|
lda (load_or_scroll_temp_pointer),y
|
|
sta (current_screen_line_ptr),y
|
|
dey
|
|
bpl cl_inner
|
|
|
|
;; Decrement all pointers by 40
|
|
;; Low bytes are in common pairs
|
|
|
|
;; Old source is new destination
|
|
;; Use different registers to minimise byte similarity with C64 KERNAl
|
|
ldy load_or_scroll_temp_pointer+1
|
|
sty current_screen_line_ptr+1
|
|
lda load_save_verify_end_address+1
|
|
ldy load_or_scroll_temp_pointer+0
|
|
sta current_screen_line_colour_ptr+1
|
|
sty current_screen_line_ptr+0
|
|
sty current_screen_line_colour_ptr+0
|
|
|
|
;; Decrementing source pointers
|
|
lda load_or_scroll_temp_pointer+0
|
|
sec
|
|
sbc #<40
|
|
sta load_or_scroll_temp_pointer+0
|
|
sta load_save_verify_end_address+0
|
|
lda load_or_scroll_temp_pointer+1
|
|
sbc #>40
|
|
sta load_or_scroll_temp_pointer+1
|
|
;; convert to screen equivalent
|
|
sec
|
|
sbc HIBASE
|
|
clc
|
|
adc #>$D800
|
|
sta load_save_verify_end_address+1
|
|
|
|
dex
|
|
bne copy_line_down_loop
|
|
|
|
no_copy_down:
|
|
jsr calculate_screen_line_pointer
|
|
|
|
;; Erase newly inserted line
|
|
ldy #79
|
|
*
|
|
lda text_colour
|
|
sta (current_screen_line_colour_ptr),y
|
|
lda #$20
|
|
sta (current_screen_line_ptr),y
|
|
dey
|
|
cpy #40
|
|
bne -
|
|
|
|
rts
|
|
|
|
done_grow_line:
|
|
not_last_line:
|
|
rts
|
|
|
|
scroll_up_if_on_last_line:
|
|
|
|
;; Colour RAM is always in fixed place, so is easiest
|
|
;; to use to check if we are on the last line.
|
|
;; The last line is at $D800 + 24*40 = $DBC0
|
|
lda current_screen_line_colour_ptr+1
|
|
cmp #>$DBC0
|
|
bne not_last_line
|
|
|
|
lda current_screen_line_colour_ptr+0
|
|
cmp #<$DBC0
|
|
beq is_last_line
|
|
|
|
jsr get_current_line_logical_length
|
|
clc
|
|
adc current_screen_line_colour_ptr+0
|
|
cmp #<$DBE7-1
|
|
bcc not_last_line
|
|
|
|
is_last_line:
|
|
|
|
dec current_screen_y
|
|
|
|
;; FALL THROUGH
|
|
|
|
scroll_screen_up:
|
|
;; Now scroll the whole screen up either one or two lines
|
|
;; based on whether the first screen line is linked or not.
|
|
|
|
;; Get pointers to start of screen + colour RAM
|
|
lda HIBASE
|
|
sta current_screen_line_ptr+1
|
|
sta load_or_scroll_temp_pointer+1
|
|
lda #>$D800
|
|
sta current_screen_line_colour_ptr+1
|
|
sta load_save_verify_end_address+1
|
|
lda #$00
|
|
sta current_screen_line_ptr+0
|
|
sta current_screen_line_colour_ptr+0
|
|
|
|
;; Get pointers to screen/colour RAM source
|
|
lda screen_line_link_table+0
|
|
bmi +
|
|
lda #40
|
|
.byte $2C ; BIT $nnnn to skip next instruction
|
|
*
|
|
lda #80
|
|
sta load_or_scroll_temp_pointer+0
|
|
sta load_save_verify_end_address+0
|
|
|
|
;; Copy first three pages
|
|
ldy #$00
|
|
ldx #3
|
|
scroll_copy_loop:
|
|
lda (load_save_verify_end_address),y
|
|
sta (current_screen_line_colour_ptr),y
|
|
lda (load_or_scroll_temp_pointer),y
|
|
sta (current_screen_line_ptr),y
|
|
|
|
iny
|
|
bne scroll_copy_loop
|
|
|
|
inc load_or_scroll_temp_pointer+1
|
|
inc current_screen_line_ptr+1
|
|
inc load_save_verify_end_address+1
|
|
inc current_screen_line_colour_ptr+1
|
|
dex
|
|
bne scroll_copy_loop
|
|
|
|
;; Copy last partial page
|
|
;; We need to copy 1000-(3*256)-line length
|
|
;; = 232 - line length
|
|
lda load_or_scroll_temp_pointer+0
|
|
lda #232
|
|
sec
|
|
sbc load_or_scroll_temp_pointer+0
|
|
tax
|
|
scroll_copy_loop2:
|
|
lda (load_save_verify_end_address),y
|
|
sta (current_screen_line_colour_ptr),y
|
|
lda (load_or_scroll_temp_pointer),y
|
|
sta (current_screen_line_ptr),y
|
|
iny
|
|
dex
|
|
bne scroll_copy_loop2
|
|
|
|
;; Fill in scrolled up area
|
|
ldx load_or_scroll_temp_pointer+0
|
|
scroll_copy_loop3:
|
|
lda #$20
|
|
sta (current_screen_line_ptr),y
|
|
lda text_colour
|
|
sta (current_screen_line_colour_ptr),y
|
|
iny
|
|
dex
|
|
bne scroll_copy_loop3
|
|
|
|
;; Shift line linkage list
|
|
ldy #0
|
|
ldx #24
|
|
link_copy:
|
|
lda screen_line_link_table+1,y
|
|
sta screen_line_link_table+0,y
|
|
iny
|
|
dex
|
|
bne link_copy
|
|
;; Clear line link flag of last line
|
|
stx screen_line_link_table+24
|
|
|
|
;; Restore correct line pointers
|
|
jsr calculate_screen_line_pointer
|
|
|
|
rts
|
|
|
|
get_current_line_logical_length:
|
|
ldy current_screen_y
|
|
lda screen_line_link_table,y
|
|
bpl line_not_linked_del
|
|
lda #79
|
|
.byte $2c ; BIT absolute mode, which we use to skip the next two instruction bytes
|
|
line_not_linked_del:
|
|
lda #39
|
|
sta logical_line_length
|
|
rts
|
|
|
|
screen_advance_to_next_line:
|
|
;; Go to start of line
|
|
lda #0
|
|
sta current_screen_x
|
|
;; Advance line number
|
|
ldy current_screen_y
|
|
inc current_screen_y
|
|
|
|
jsr calculate_screen_line_pointer
|
|
|
|
;; Work out if we have gone off the bottom of the screen?
|
|
;; 1040 > 1024, so if high byte of screen pointer is >= (HIBASE+4),
|
|
;; then we are off the bottom of the screen
|
|
lda current_screen_line_ptr+1
|
|
sec
|
|
sbc HIBASE
|
|
cmp #3
|
|
bcc +
|
|
|
|
;; Off the bottom of the screen
|
|
jsr scroll_screen_up
|
|
|
|
*
|
|
jmp chrout_done
|
|
|
|
add_40_to_screen_x:
|
|
lda current_screen_x
|
|
clc
|
|
adc #40
|
|
sta current_screen_x
|
|
rts
|
|
|
|
normalise_screen_x_y:
|
|
;; Normalise X and Y values
|
|
|
|
;; If X < 0, then make X = X + 40 (or 80, if previous line is linked)
|
|
* lda current_screen_x
|
|
bpl x_not_negative
|
|
dec current_screen_y
|
|
;; Check that we didn't go backwards off the top of the screen
|
|
bpl +
|
|
lda #0
|
|
sta current_screen_y
|
|
|
|
* jsr add_40_to_screen_x
|
|
|
|
;; Check if line is linked, if so, add 40 again
|
|
ldy current_screen_y
|
|
lda screen_line_link_table,y
|
|
bpl +
|
|
jsr add_40_to_screen_x
|
|
*
|
|
|
|
x_not_negative:
|
|
;; Work out if X is too big
|
|
ldy current_screen_y
|
|
lda screen_line_link_table,y
|
|
bpl +
|
|
lda #79
|
|
.byte $2C ; BIT $nnnn, used to skip next instruction
|
|
* lda #39
|
|
cmp current_screen_x
|
|
bcs x_not_too_big
|
|
|
|
;; X value is too big, so subtract 40 and increment Y
|
|
lda current_screen_x
|
|
sec
|
|
sbc #40
|
|
sta current_screen_x
|
|
inc current_screen_y
|
|
|
|
x_not_too_big:
|
|
|
|
;; Make sure Y isn't negative
|
|
lda current_screen_y
|
|
bpl +
|
|
lda #0
|
|
sta current_screen_y
|
|
*
|
|
;; Make sure Y isn't too large for absolute size of
|
|
;; screen
|
|
cmp #24
|
|
bcc +
|
|
lda #24
|
|
sta current_screen_y
|
|
*
|
|
;; Make sure Y isn't too much for the screen, taking
|
|
;; into account the line link table
|
|
ldy #24 ; max allowable line
|
|
ldx #0
|
|
link_count_loop:
|
|
lda screen_line_link_table,x
|
|
bpl +
|
|
dey
|
|
* inx
|
|
cpx #25
|
|
bne link_count_loop
|
|
tya
|
|
cmp current_screen_y
|
|
bcs y_ok
|
|
sta current_screen_y
|
|
y_ok:
|
|
|
|
rts
|
|
|
|
calculate_screen_line_pointer:
|
|
jsr normalise_screen_x_y
|
|
|
|
;; Reset pointer to start of screen
|
|
lda HIBASE
|
|
sta current_screen_line_ptr+1
|
|
lda #0
|
|
sta current_screen_line_ptr+0
|
|
|
|
;; Add 40 for every line, or 80 if the lines are linked
|
|
ldx current_screen_y
|
|
*
|
|
;; Stop if we have counted enough lines
|
|
beq +
|
|
|
|
;; Add 40 or 80 based on whether the line is linked
|
|
;; or not.
|
|
ldy #40
|
|
;; -1 offset is because we count down from N to 1, not
|
|
;; N-1 to 0.
|
|
lda screen_line_link_table-1,x
|
|
bpl cslp_l1
|
|
ldy #80
|
|
cslp_l1:
|
|
;; Add computed line length to pointer value
|
|
tya
|
|
clc
|
|
adc current_screen_line_ptr+0
|
|
sta current_screen_line_ptr+0
|
|
lda current_screen_line_ptr+1
|
|
adc #0
|
|
sta current_screen_line_ptr+1
|
|
|
|
;; Loop back to next line
|
|
dex
|
|
jmp -
|
|
*
|
|
|
|
;; FALL THROUGH
|
|
|
|
update_colour_line_pointer:
|
|
;; Now setup pointer to colour RAM
|
|
lda current_screen_line_ptr+0
|
|
sta current_screen_line_colour_ptr+0
|
|
lda current_screen_line_ptr+1
|
|
sec
|
|
sbc HIBASE
|
|
clc
|
|
adc #>$d800
|
|
sta current_screen_line_colour_ptr+1
|
|
|
|
rts
|
|
|
|
|
|
advance_screen_pointer_40_bytes:
|
|
lda current_screen_line_ptr+0
|
|
clc
|
|
adc #<40
|
|
sta current_screen_line_ptr+0
|
|
sta current_screen_line_colour_ptr+0
|
|
lda current_screen_line_ptr+1
|
|
adc #>40
|
|
sta current_screen_line_ptr+1
|
|
sec
|
|
sbc HIBASE
|
|
clc
|
|
adc #>$D800
|
|
sta current_screen_line_colour_ptr+1
|
|
|
|
jmp update_colour_line_pointer
|
|
|
|
colour_codes:
|
|
;; CHR$ codes for the 16 colours
|
|
.byte 144,5,28,159,156,30,31,158
|
|
.byte 129,149,150,151,152,153,154,155
|